% \iffalse % ------------------------------------------------------------------- % % Copyright 2002--2006, Daniel H. Luecking % % Mfpic may be distributed and/or modified under the conditions of the % LaTeX Project Public License, either version 1.3b of this license or (at % your option) any later version. The latest version of this license is in % % and version 1.3b or later is part of all distributions of LaTeX version % 2003/12/01 or later. % % Mfpic has maintenance status "author-maintained". The Current Maintainer % is Daniel H. Luecking. There are several Base Interpreters: plain TeX, LaTeX, % plain Metafont and plain MetaPost. % %<*driver> \ProvidesFile{grafbase.dtx} [2006/05/26 v0.9. Metafont/post macros to interface with mfpic.]% \documentclass[draft]{ltxdoc} \usepackage{docmfp} \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{meta\-font}} \def\MP{\prog{meta\-post}} \def\PS{\prog{Post\-Script}} \def\CMF{\prog{Meta\-font}} \def\CMP{\prog{Meta\-post}} \def\opt#1{{\sffamily\upshape#1}} \def\mfc#1{{\mytt#1}} \let\env\mfc \let\file\mfc \let\gbc\mfc \renewcommand\{{{\mytt\char`\{}} \renewcommand\}{{\mytt\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\usage#1{\textrm{#1}} \def\index@prologue{\section*{Index}\markboth{Index}{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} \def\close@crossref{\SpecialEscapechar{:}} \makeatother \def\VariableIndex#1{\SpecialMfpIndex{#1}{\variablestring}{}} \def\RoutineIndex #1{\SpecialMfpIndex{#1}{}{}} \def\pdfTeX{\textrm{pdf\kern.04em\TeX}} \def\pdfLaTeX{\textrm{pdf\kern.06em\LaTeX}} \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} \SpecialEscapechar{:} \def\bslash{:} \DisableCrossrefs \CodelineIndex \AlsoImplementation \begin{document} \DeleteShortVerb{\|} \DocInput{grafbase.dtx} \end{document} % %\fi % % \CheckSum{1369} % \CharacterTable % {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z % Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z % 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 % \GetFileInfo{grafbase.dtx} % \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 more alike than different (96\% of the code is % identical), we document both here. % % This file documents the \grafbase{} source code. The user manual for % \mfpic{} is distributed as \file{mfpman.pdf} produced from % \file{mfpman.tex}. % \end{abstract} % % \StopEventually{\PrintIndex} % \tableofcontents % % \section{Introduction}\label{intro} % % \subsection{Identification and checks}\label{checks} % % \DescribeVariable{grafbaseversion} We use \mfc{grafbaseversion} to % check if \prog{grafbase} has been previously loaded, later we use it % to check a mismatch with the version of \mfpic{} (if used). % \gbc{grafbase} was used in previous versions. If either is known, we % bail out. The \gbc{grafbase} boolean is really never needed, but it % has been around since I took over. It is possible to write files that % change behavior when \grafbase{} is loaded, however if they say % ``\gbc{if grafbase:}, they can only be called with \gbc{grafbase} a % known boolean. % % \VariableIndex{fileversion} \gbc{fileversion} and % \VariableIndex{filedate} \gbc{filedate} provide identifying information. % % \DescribeRoutine{GBmsg} % These are used fairly consistently and identify the source of the % message delivered as being `\gbc{Grafbase}'. \DescribeRoutine{GBwarn} % Warnings are delivered by \gbc{GBwarn}. The takes care of both the % \DescribeRoutine{GBerrmsg}\gbc{GBerrmsg}error message and the % \mfc{errhelp} string. % \begin{macrocode} %<*MF|MP> if (known grafbaseversion) or (known grafbase): message "Grafbase (" & jobname & "): You have loaded grafbase more " & "than once! Please make sure that it is loaded only once."; endinput; fi boolean grafbase; grafbase := true; string fileversion, filedate; fileversion := "0.9"; filedate := "2006/05/26"; message " Loading grafbase macros, version " & fileversion & " " & filedate & "."; def GBmsg expr s = message "Grafbase (" & jobname & "): " & s; enddef; def GBwarn expr s = GBmsg "Warning, " & s; enddef; def GBerrmsg (expr s) expr t = errhelp t; errmessage "Grafbase (" & jobname & "): " & s; errhelp ""; enddef; % \end{macrocode} % % \DescribeVariable{MFPIC} % The \gbc{MFPIC} variable is not yet used at all. % It might be possible (at some point) to optimize things for \mfpic{} use % whenever \gbc{mfpicversion} is defined, but so far we don't do anything % except test the version and set this boolean. % % \DescribeVariable{mfpicversion} % The output file written by the \mfpic{} macros includes a test that % \gbc{mfpicversion} and \mfc{grafbaseversion} are the same, % but that would fail to catch a new \grafbase{} with an old \mfpic. So % we also put a test here with \DescribeRoutine{checkversions} % \gbc{checkversions}, though it alone would fail to catch the use % of older versions of \grafbase{} with current versions of \mfpic. Newer % versions of \mfpic{} signal their version before inputting % \file{grafbase}. Unfortunately (for error checking), \grafbase{} can % also be used without \mfpic, so if \gbc{mfpicversion} is unknown, we % merely write a message. % \begin{macrocode} boolean MFPIC; MFPIC := false; def checkversions (expr g)= numeric grafbaseversion; grafbaseversion := g; if unknown mfpicversion: % no mfpic, or < 0.63 GBmsg "Recent mfpic not detected."; elseif g = mfpicversion: MFPIC := true; else: message ""; GBwarn "Version mismatch: " & "mfpic and grafbase versions do not match."; message ""; fi enddef; checkversions (90); % \end{macrocode} % We try to make sure that the macros of \file{plain.mf} or % \file{plain.mp} (where \mfc{base_name} is defined to be \mfc{"plain"}) % are available. % \begin{macrocode} if unknown base_name : input plain; elseif not string base_name: input plain; elseif base_name <> "plain": input plain; fi % \end{macrocode} % % \DescribeVariable{METAPOST} % We try to determine which of \MF{} or \MP{} is using these macros. % Perhaps one day we'll merge both versions of \grafbase{} in one file and % use the following boolean. For now, we only use it to catch cases where % 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 Geamparalele din Babadag: METAPOST := true; else: METAPOST := false; fi %<*MF> if METAPOST: GBerrmsg ("wrong compiler.") "This file is for Metafont. For Metapost use grafbase.mp."; fi % %<*MP> if not METAPOST: GBerrmsg ("wrong compiler.") "This file is for Metapost. For Metafont use grafbase.mf."; fi % % \end{macrocode} % % \DescribeRoutine{GBdebug} % The \gbc{debug} flag is for developers, who should set it before % inputing \file{grafbase}. % \DescribeRoutine{GBenddebug} % These two routines start and end debug messages. % % \DescribeRoutine{mftitle} % The \gbc{mftitle} macro is useful when debugging. % It will put its argument, which should be a string, as a TFM comment, % and also print it to the terminal and log file. % \begin{macrocode} if 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 = if string t: t; message t; fi enddef; % \end{macrocode} % % \subsection{Setting up the font, \MF{} only}\label{font} % % Font-related housekeeping is only for \MF{}. \MF{} only produces % fonts, so we have to define the variables it thinks are needed for % fonts. % % \DescribeVariable{GBgeneric} % We intercept the \mfc{mode} variable before \mfc{mode_setup} can set % \mfc{proof} mode. We used to set \mfc{mode := cx} (and later % \mfc{ljfour}) if it was unknown. For a while we just issued an error % message. In this version we define a 600dpi mode called \mfc{GBgeneric} % as a fallback (neither \gbc{mode} nor \gbc{localfont} known). % % The font identifier and coding scheme are just for information and end % up as comments in the \file{.tfm} file (in all capitals). The design % 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 ("Metafont mode is unknown.") "Set mode to a known mode. Perhaps localfont or ljfour. " & "If you proceed, a generic 600dpi mode will be used."; if known localfont: mode := localfont; else: mode_def GBgeneric = proofing := 0; fontmaking := 1; tracingtitles := 0; if unknown pixels_per_inch: pixels_per_inch := 600; fi blacker := 0; fillin := 0; o_correction := 1; enddef; mode := GBgeneric; fi fi mode_setup; if debug: GBdebug; >> "pixels_per_inch = " & decimal pixels_per_inch; GBenddebug; fi font_identifier := "MFpic graphics"; font_coding_scheme := "Arbitrary"; interim designsize := 128pt#; % % \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} %pt# := pt; bp# := bp; %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 \emph{must} % be after \mfc{mode_setup}. Also don't complain if a clockwise path is % filled (only \MF{} does this). % \begin{macrocode} interim warningcheck := 0; %interim turningcheck := 0; % \end{macrocode} % % \subsection{Initializations}\label{init} % % \VariableIndex{unitlen} % \VariableIndex{xscale} % \VariableIndex{yscale} % \VariableIndex{xneg} % \VariableIndex{xpos} % \VariableIndex{yneg} % \VariableIndex{ypos} % The following are the various variables determining the extent of a % picture. These variables would normally be set by a user for each % picture, or by \mfpic, but we give them default values anyway. They % give a nominal picture size of one inch with a graph unit corresponding % to $1/10$ inch. % \begin{macrocode} numeric unitlen, xscale, yscale, xneg, xpos, yneg, ypos; unitlen := 1 bp#; xscale := 7.2; yscale := 7.2; xneg := 0; xpos := 10; yneg := 0; ypos := 10; % \end{macrocode} % % \DescribeVariable{deg}\VariableIndex{degree} % We support both degrees and radians for angles. In \MF, one degree is % the unit of angle. % \DescribeVariable{radian} % One radian is $180/\pi$ degrees. We also define \gbc{pi} so a user can say % \gbc{90} or \gbc{90deg} or \gbc{pi/2*radian} % \DescribeVariable{pi} for the same effect. % Actually, not quite: because of \MF{}'s precision limits, the latter is % about 90.00025 degrees. \MF{}'s precision is 16 binary places, or % slightly under 5 decimals. The accuracy of \gbc{pi} and \gbc{radian} is % the maximum possible. If we \emph{define} \gbc{radian} to be % \gbc{90/(pi/2)} or \gbc{180/pi} the value of \gbc{pi/2*radian} is even % less accurate. % \begin{macrocode} newinternal deg, pi, radian; deg := 1; pi := 3.14159; radian := 57.29578; numeric degree; degree := deg; % \end{macrocode} % % \VariableIndex{drawpen} % \VariableIndex{penwd} % \DescribeRoutine{resizedrawpen} % Since we need to do this frequently, we define a macro that changes the % pen width for subsequent drawing. This enables the file written by % \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.) % % \VariableIndex{hatchpen} % We could do this for the hatching pen, but it doesn't seem to change as % often. The \mfc{pickup} command performs \mfc{yscaled aspect_ratio}, but % so does the \gbc{shpath}, the only other place pens are required. In % 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. % % \VariableIndex{hatchwd} % The default \gbc{hatchwd} used to be larger, but it seemed ugly to me. % (Backward compatibility? What's that?). % \begin{macrocode} newinternal penwd; penwd := 0.5pt; 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} % % \DescribeVariable{clipall} % We have two booleans related to clipping. One, \gbc{clipall} is meant to % be turned on just once (per picture), and it causes the \gbc{endmfpic} % code to clip the current picture to the boundaries defined by the % picture size variables. The other, % \DescribeVariable{ClipOn} % \gbc{ClipOn}, is meant to be turned on and off. While on, most drawing % macros (all?) will clip their result to the current \emph{clipping path % array}. % \DescribeVariable{ClipPath} % The clipping path array is an array of paths: \gbc{ClipPath[\,]} together % with a numeric \gbc{ClipPath}. The numeric variable contains the number % of clipping paths; the paths are \gbc{ClipPath[1]} through % \gbc{ClipPath[ClipPath]}. A macro later on is defined to loop through % the array, clipping the current picture to the union of their interiors. % % \DescribeVariable{truebbox} % The \gbc{truebbox} boolean sets the bounding box of the picture to its % natural size in \MP. The default behavior of \MP{} is to output a % bounding box that is the natural size of the graphic. The \grafbase{} % 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 ( setboolean (ClipOn) false; t) enddef; % \end{macrocode} % % \DescribeVariable{showbbox} % The boolean \gbc{showbbox} is for debugging the \gbc{*bbox} macros. % \begin{macrocode} boolean showbbox; showbbox := false; % \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 define \MF{} replacements for some of the \MP{} color variables and % macros. Our point of view will be: make each color variable a numeric in % \MF{}. Each will lie between $0$ and $1$ representing shades of gray. % For \emph{drawing} commands we will only distinguish between nonwhite % (black, ${}<1$) and white (${}\ge1$). For filling commands we will allow % levels in between, and fill with an approximation using a version of % the \gbc{shade} macro. % \begin{macrocode} %<*MF> let color = numeric; color black, white; black := 0; white := 1; def withcolor text t = enddef; % def _wc_ = withcolor enddef; % \end{macrocode} % % \VariableIndex{currentcolor} % \VariableIndex{drawcolor} % \VariableIndex{fillcolor} % \VariableIndex{hatchcolor} % \VariableIndex{headcolor} % \VariableIndex{pointcolor} % \VariableIndex{tlabelcolor} % We also define some color variables whose names reflect their use. % Thus, \gbc{fillcolor} is used for filling, etc. The color % \gbc{currentcolor} isn't used anywhere yet. The color % \mfc{background} is used in \MP{} for unfilling a region. % \begin{macrocode} color currentcolor, drawcolor, fillcolor, 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. It does % \emph{not} truncate the parameters, so if that is necessary, use % \gbc{rgb}. % % \DescribeRoutine{rgb} % To simplify \mfpic, we have the nearly redundant \gbc{rgb} which % 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) = % gray (sqrt((2r*r + 4g*g + b*b)/7)) % (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} % These last two, like \gbc{rgb}, are nearly redundant, but they convert % numerics to gray, and convert other non-color variables and unknown % color variables to black. % \DescribeRoutine{forceclr} % The difference between \gbc{named} and \gbc{forceclr} is that the % former requires a suffix parameter (that is, a \emph{name}), while the % latter takes an expression. It may be that the latter will never be % needed, but for a time it seemed there were cases where we ought to use % it to force an expression to be a color. % \begin{macrocode} vardef RGB (expr R, G, B) = rgb (R/255, G/255, B/255) enddef; vardef named (suffix c) = if unknown c: black else: forceclr (c) fi enddef; vardef forceclr (expr c) = if 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 typical example of an array. Arrays are based on the % fact that a variable can be of a different type from (and can be almost % completely unrelated to) the variables formed by putting numeric % suffixes on it. % % \DescribeRoutine{list} % The \gbc{list} macro is essentially due to Frank Michielsen, and assigns % a \emph{list} (i.e., a comma separated sequence of expressions) to an % array. Note that the items in the list have to be the same type, and the % same type as \mfc{v[\,]}. But \mfc{v} itself must be numeric. % % \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 (which I've since forgotten) and so I added the % \gbc{_map} variable that skips the comma on the first time through the % loop. This routine is currently only used in the code that \mfpic's \ % \cs{plr} writes. % \begin{macrocode} def list (suffix v) (text lst) = v := 0; for _itm = lst: v[incr v] := _itm; endfor if v = 0: GBerrmsg ("no list to process!") "An attempt was made to produce an array from a " & "list of expressions having no valid entries."; fi enddef; def map (text proc) (text lst) = hide (_map := 0;) for _a = lst: if _map = 0: hide (_map := 1;) else: , fi proc (_a) endfor enddef; % \end{macrocode} % \DescribeRoutine{knownnumericarray} % Checks if a suffix is the name of an array. Requires \gbc{arr} to be a % known positive integer, and all the variables \gbc{arr[n]} to be known % for \gbc{n} from 1 to \gbc{arr}. Since we so far only need it for % numeric arrays, we also check if each entry is numeric. % \begin{macrocode} vardef knownnumericarray suffix arr = setboolean (_kna) (known arr) and (numeric arr); if _kna : _kna := (arr = floor arr) and (arr >= 1); for _idx = 1 upto arr : exitif not _kna; _kna := (known arr[_idx]) and (numeric arr[_idx]); endfor fi _kna enddef; % \end{macrocode} % % \DescribeRoutine{copyarray} % This makes some code much more readable. It simply steps through an % array and copies the values into another array. It is only used for % numeric arrays so far, but could be used for any kind. % \begin{macrocode} def copyarray (suffix src, dest) = for _idx = 1 upto src: dest[_idx] := src[_idx]; endfor dest := src; enddef; % \end{macrocode} % % \DescribeRoutine{maparr} % The \gbc{maparr} macro applies a procedure \gbc{proc} to each member of % array \gbc{p[\,]} with \gbc{p} members. It returns nothing. It is currently % unused, although it was once used for things like \gbc{maxpair}. % \begin{macrocode} def maparr (text proc) (suffix p) = for _idx = 1 upto p: proc (p[_idx]); endfor 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} in this macro, % but ran into a problem once when the argument had a suffix. You can't % apply \mfc{save} to a variable with a suffix. Moreover, `\mfc{save p}' % also renders \mfc{p.x} unknown, so I judged it best to let whoever calls % this macro decide what to save. Actually, now it expands to the more % general command \gbc{gsetarray} with type \gbc{pair}. That command % then reads the suffix argument that should follow. % % Since the above change was made, macros evolved so that \emph{all} uses % of \gbc{textpairs} are now preceeded by \gbc{save}. Thus, I have now % replaced them all with calls to \gbc{setpairs} (it calls \gbc{setarray} % \emph{does} \gbc{save} the variable). In all those cases, the % `\gbc{saved}' variable is a temporary local array. % % \DescribeRoutine{setuniquepairs} % This does the same but omit any pair if it is identical to the previous % one. It \mfc{save}\,s the variable, since all its uses are internal % and require that. % \begin{macrocode} def textpairs = gsetarray (pair) enddef; def setuniquepairs (suffix p) (text t) = save p; pair p[]; setpairs (_up) (t); if _up > 0: p := 1; p1 := _up1; for _i = 2 upto _up: if _up[_i] <> p[p]: p[incr p] := _up[_i]; fi endfor else: p := 0; fi enddef; % \end{macrocode} % % \subsection{Utilities}\label{utilities} % % \DescribeRoutine{chpair} % This applies a procedure \gbc{proc} (which maps numeric to numeric) to % each part of pair \gbc{p}, and returns the resultant pair. I've decided % not to use it (for efficiency), but to leave it defined for backward % compatibility.\\ % \DescribeRoutine{floorpair}\gbc{floorpair} applies \mfc{floor} to both % parts of a pair.\\ % \DescribeRoutine{ceilingpair}\gbc{ceilingpair} does the same with % \gbc{ceiling}.\\ % \DescribeRoutine{hroundpair}\gbc{hroundpair} does the same with % \gbc{hround}. % % All three could use \gbc{chpair} with \gbc{proc} equal to \mfc{floor}, % \mfc{ceiling} and \mfc{hround}, but I now code them directly. % % \DescribeRoutine{goodpair} % This last one is used (only in \MF{}) to adjust pairs to the pixel grid. % It is the only place \gbc{hroundpair} is used. None of these is used in % the \MP{} version. % \begin{macrocode} vardef chpair (text proc) (expr p) = (proc (xpart p), proc (ypart p)) enddef; vardef floorpair (expr p) = (floor (xpart p), floor (ypart p)) enddef; vardef ceilingpair (expr p) = (ceiling (xpart p), ceiling (ypart p)) enddef; %<*MF> def hroundpair (expr p) = (hround (xpart p), hround (ypart p)) enddef; vardef goodpair (expr p) = hroundpair(p.t_) enddef; % % \end{macrocode} % % \DescribeRoutine{emin} % \gbc{emin} differs from \prog{plain}'s \mfc{min} in that it allows % only two values. It can therefore be coded simply, without the overhead % of a \mfc{for}-loop. \DescribeRoutine{emax}\gbc{emax} is analogous. Both % are needed so often that it is possible a significant amount of time is % saved with these versions. % % \DescribeRoutine{pairmin} % \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 % \DescribeRoutine{pairmax}\gbc{pairmax} is analogous, producing the maximum. % % \DescribeRoutine{minpair} % The \gbc{minpair} macro returns the pair comprising the minimum $x$ and % minimum $y$ coordinates of all pairs in the array \gbc{p[\,]}. % \DescribeRoutine{maxpair} % \gbc{maxpair} is analogous. Somehow, both of them have disappeared from % \grafbase. They were formerly used only in the \gbc{*bbox} macros. % That code used a loop to build an array of control points and these % routines would \emph{each} loop through that. The current code uses one % loop (instead of three) through the control points, updating both the % maximum and minimum at each one. % \begin{macrocode} vardef emin (expr a, b) = if a < b: a else: b fi enddef; vardef emax (expr a, b) = if a > b: a else: b fi enddef; vardef 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) = setpair (_mp) p1; for _idx = 2 upto p - 1: _mp := pairmin (_mp, p[_idx]); endfor pairmin (_mp, p[p]) enddef; vardef maxpair (suffix p) = setpair (_mp) p1; for _idx = 2 upto p - 1: _mp := pairmax (_mp, p[_idx]); endfor pairmax (_mp, p[p]) enddef; % \end{macrocode} % % \DescribeRoutine{xprod} % A binary operation between pairs $z\sb1$ and $x\sb2$ that returns the % cross product $x\sb1 y\sb2 - x\sb2 y\sb1$. This gives, among other % things, twice the area of the triangle with two sides $z\sb1$ and % $z\sb2$. It is used only in \gbc{mkconvex}. % \begin{macrocode} primarydef Z xprod W = (xpart Z * ypart W - xpart W * ypart Z) enddef; % \end{macrocode} % % \DescribeRoutine{force_initial} % \gbc{force_initial} modifies a path so that it has all the same points % and controls as before, except its first point is replaced with \mfc{p}. % \DescribeRoutine{force_terminal}\gbc{force_terminal} replaces the last % point. This is for cases where, theoretically, paths \gbc{f} and \gbc{g} % should meet at an endpoint, but do not due to finite precision. Instead % of doing \mfc{f..g}, which adds a random tiny segment, we adjust the % endpoints to exactly match the other and do \mfc{f\&g}, producing a join % without an additional segment. % % \DescribeRoutine{force_equal_ends} % This forces the last point of the first path and the first point of the % second to equal the average of their original values. It is the only one % of these four actually used anywhere else in \grafbase. % \DescribeRoutine{replace_ends_of_cycle}\gbc{replace_ends_of_cycle} % applies something similar to a cycle. % \begin{macrocode} def force_initial (expr p) (suffix f) = hide( setnumeric (_n) length f; f := p if _n = 0: {0,0} else: ..controls post0 (f) and pre 1 (f).. subpath (1,_n) of f fi;) enddef; def force_terminal (expr p) (suffix f) = hide(setpath (_f) reverse f; force_initial (p) (_f); f := reverse _f;) enddef; def force_equal_ends (suffix f, g) = hide(save _p; pair _p; _p := .5[pnt[length f] (f), pnt0(g)]; force_terminal (_p) (f); force_initial (_p) (g);) enddef; def replace_ends_of_cycle (expr p) (suffix f) = hide( if cycle f: save _n; _n := length f; f := p if _n = 0: &cycle else: .. controls post0 (f) and pre 1 (f) .. if _n = 1: cycle else: subpath (1, _n - 1) of f .. controls post[_n - 1](f) and pre[_n](f) .. cycle fi fi; fi) enddef; % \end{macrocode} % % \DescribeRoutine{intersects} % A binary relation, with the precedence level (almost) that of other % relations, produces \mfc{true} if \MF{} determines that the paths % intersect, false otherwise. It also \DescribeVariable{thetimes}sets the % pair variable \gbc{thetimes} and its parts \gbc{_Xtime} and \gbc{_Ytime}. % Then \DescribeRoutine{misses}\gbc{misses} is the opposite relation, % used when the intersection point is not needed. It only occurs in the % (unused) code of \gbc{tightbbox}. % \begin{macrocode} pair thetimes; numeric _Xtime, _Ytime; tertiarydef a intersects b = begingroup thetimes := a intersectiontimes b; _Xtime := xpart thetimes; _Ytime := ypart thetimes; (_Xtime > -1) endgroup enddef; tertiarydef a misses b = ((a intersectiontimes b) < origin) enddef; % \end{macrocode} % % \DescribeRoutine{makepicture} % Takes any expression and does what it can to make a picture from it. % % \DescribeRoutine{onepointpath} % Takes a point and forces it to be a path. If a vardef takes a list of % points and it \emph{must} return a path that perhaps \emph{must} be % cyclic, it can use this as a fallback. If an \mfpic{} command such as % \cs{arc} receives an invalid optional parameter, it won't know what % command to write to the output file. It can use % \DescribeRoutine{fallbackpath}\gbc{fallbackpath} as long as the first % parameter is a point. % % \DescribeRoutine{even} % Of course \gbc{even} means \gbc{not odd}. % \begin{macrocode} vardef makepicture (expr s) = if picture s: s % elseif string s: s infont defaultfont scaled defaultscale elseif path s: picpath (s) else: nullpicture fi enddef; vardef onepointpath (expr cyclic, q) = q if cyclic: &cycle else: {0,0} fi enddef; vardef fallbackpath (expr cyclic, p) (text t) = onepointpath (cyclic, p) enddef; def even = not odd enddef; primarydef a divides b = ((b mod a) = 0) enddef; % \end{macrocode} % % \DescribeRoutine{image} % The \mfc{image} macro exists in \file{plain.mp} but not \file{plain.mf}. % The purpose is to just use the \file{plain} \MF{} and \grafbase{} macros % as you normally would, but wrap the whole thing in parentheses preceded % by \gbc{X := image} to get all those things drawn on the picture % variable \gbc{X}. % % \DescribeRoutine{beginimage} % Instead of making lengthy drawing code a parameter, one might prefer an % environment-like syntax, writing \gbc{X := beginimage } at the start % and \DescribeRoutine{endimage}\gbc{endimage} at the end. % % \DescribeRoutine{makeimage} % This is for the \mfpic{} command \cs{mfpimage}. It takes a suffix % parameter (the name of the picture variable) and a coordinate pair (in % graph coordinates). The drawing commands, up to the following % \gbc{endimage}, draw on this picture variable with the given pair as the % reference point. % \begin{macrocode} %<*MF> vardef image (text t) = newpicture (currentpicture); t; currentpicture enddef; % def beginimage = begingroup newpicture (currentpicture); enddef; def endimage = ; currentpicture endgroup enddef; def makeimage (suffix name) (expr refpt) = setpair (_image_reference_point) zconv(refpt); setpicture (name) beginimage enddef; def concludeimage = endimage shifted % -goodpair (_image_reference_point) % -_image_reference_point enddef; % \end{macrocode} % % \DescribeRoutine{setvariable} % This is are mainly to save space in \mfpic-generated files. In \grafbase{} % itself the \mfc{save} is often inconvenient, but it turns out there are % many cases where it \emph{is} used; enough so that we have abbreviations % \RoutineIndex{setpicture}\gbc{setpicture}, % \RoutineIndex{setpath}\gbc{setpath}, \RoutineIndex{setpair}\gbc{setpair} % and \RoutineIndex{setboolean}\gbc{setboolean}, together with the % common uses \RoutineIndex{newpicture}\gbc{newpicture} and % \RoutineIndex{convertpath}\gbc{convertpath}. % \DescribeRoutine{gsetvariable}\gbc{gsetvariable} is the global % version. It has no abbreviations, but it is occasionally needed for % \mfpic{}. The only difference is the lack of a \gbc{save}. None of these % commands take the value as a parameter. That should follow, and is picked % up by the ending \mfc{:=}. % % \DescribeRoutine{setarray} % Then \gbc{setarray} is the array version. It takes the same parameters % as \gbc{setvariable}, but what should follow is a list of expressions in % parentheses. It calls \gbc{list} to read each item into % \gbc{name1}, \gbc{name2}, etc. % \DescribeRoutine{setpairs}\gbc{setpairs} is an abbreviation for arrays % of pairs. There is also has a global version % \DescribeRoutine{gsetarray}\gbc{gsetarray}. % \begin{macrocode} def setvariable (text kind) (suffix name) = save name; kind name; name := enddef; def gsetvariable (text kind) (suffix name) = kind name; name := enddef; def setnumeric (suffix name) = save name; name := enddef; def setboolean = setvariable (boolean) enddef; def setpair = setvariable (pair) enddef; def setpath = setvariable (path) enddef; def setcolor = setvariable (color) enddef; def setpicture = setvariable (picture) enddef; def settension (suffix tn) expr tens = setnumeric (tn) if tens > 0: tens else: default_tension fi; enddef; def fixtension (suffix tn) = if tn < .75: tn := .75; fi enddef; def newpicture (suffix pic) = setpicture (pic) nullpicture; enddef; def convertpath (suffix g) expr f = setpath (g) zconv (f); enddef; def setarray (text kind) (suffix name) = save name; kind name[]; list (name) enddef; def setpairs = setarray (pair) enddef; def gsetarray (text kind) (suffix name) = numeric name; kind name[]; list (name) enddef; % \end{macrocode} % % The next are slightly different, but seem to belong here. % \DescribeRoutine{setbbox} % In \gbc{setbbox} we save and initialize \emph{two} pair variables and % set them to the bounding box of a path that should follow. % % \DescribeRoutine{setsplit} % There are a couple of routines that modify a variable to make sure it is % positive and integral. In a couple of places two routine \emph{must} use % the same value. Here we isolate the code that does the modification, and % then both routines call \gbc{setsplit}. % \begin{macrocode} def setbbox (suffix ll, ur) = save ll, ur; pair ll, ur; getbbox (ll, ur) enddef; def setsplit (suffix s) expr ss = setnumeric (s) emax (1, ceiling ss); enddef; % \end{macrocode} % % \section{The \grafbase{} Coordinate System}\label{coordinate} % % 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 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 either should be independent of scale % or should scale in a nonobvious way. The diameter of the drawing pen is % one of the latter things, so the default pen width is in device units. % Also for the hatching pen. % % When drawing a path we want to use device coordinates. When defining % paths, we typically want to use graph coordinates. The macros that do % the drawing, therefore, need to convert from one to the other. In % addition, for inclusion of the picture in a \TeX{} document, we normally % want the lower left corner of the graph space to have device coordinates % $(0,0)$. % % \subsection{The main transforms}\label{ztr} % % \DescribeVariable{vtr} % We therefore have two transforms: \gbc{vtr} is the \emph{vector} or % linear transform for pair quantities that remain invariant under shifts, % and \DescribeVariable{ztr}\gbc{ztr} is a \emph{point} or affine % transformation for pair quantities that change appropriately under % shifts. % % The quantities \gbc{xneg}, \gbc{xpos}, \gbc{yneg}, and \gbc{ypos} are % in \emph{graph} coordinates. Shifting by \gbc{(-xneg, -yneg)} transforms % the lower left corner to $(0,0)$. Multiplication by \gbc{xscale} and % \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). % % In \MF{}, \mfc{currenttransform} (via the macro \mfc{.t_}, defined by % \mfc{mode_setup}) takes care of the aspect ratio. In \MP{} the final % rasterizer should do this. % % \gbc{charwd} and \gbc{charht} are sharped coordinates defined by the % startup code \gbc{beginmfpic}, while \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; % %<*MP> >> "w_ = " & decimal w_ & "bp"; >> "h_ = " & decimal h_ & "bp"; >> "unitlen = " & decimal unitlen & "bp"; % >> "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 is"; show ztr; >> "vtr is"; show vtr; GBenddebug; fi enddef; % \end{macrocode} % % \DescribeRoutine{zconv} % The macro \gbc{zconv} converts a variety of expressions from graph to % device coordinates. The expressions include pairs, paths, and transforms. % This is an affine transform. The inverse, % \DescribeRoutine{invzconv}\gbc{invzconv}, converts a variety of % expressions from device to graph coordinates. % % \DescribeRoutine{vconv} % The vector version, \gbc{vconv}, converts a vector \gbc{v} from graph to % device coordinates. This is a linear (ie, vector) transform. Also, % \DescribeRoutine{invvconv}\gbc{invvconv} converts a vector from device % to graph coordinates. % \begin{macrocode} vardef zconv (expr a) = a transformed ztr enddef; vardef invzconv (expr a) = a transformed (inverse ztr) enddef; vardef vconv (expr v) = v transformed vtr enddef; vardef invvconv (expr v) = v transformed (inverse vtr) enddef; % \end{macrocode} % % \subsection{The \gbc{mfpic} environment}\label{mfpic} % % \DescribeRoutine{active_plane} % \gbc{active_plane} is the active drawing plane. \mfc{currentpicture} is % unknown at this stage (because it's set in \gbc{beginmfpic}). We use a % \mfc{def}, and not a picture assignment, partly for this reason but also % because we can achieve special effects by redefining it (see the % \gbc{tile} macro). % \begin{macrocode} def active_plane = currentpicture enddef; % \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{underlaylabels} is true, we try to make them part of the % background, adding them to the picture variable \gbc{background_labels}. % Just before shipout, the picture is placed on top. % % If \gbc{overlaylabels} is \gbc{true}, we try to make labels in \MP{} % behave the same as labels in \TeX{} (for \mfpic) by adding the labels % on last. We do this by adding them to the picture variable % \gbc{foreground_labels} as they occur, then add that picture onto % \gbc{active_plane} just before shipout. For backward compatibility, % the default for \gbc{overlaylabels} is \gbc{false}. % % We initialize \gbc{foreground_labels} and \gbc{background_labels} here. The % pair variables \gbc{labelbb.ll} and \gbc{labelbb.ur} keep track of the % bounding box of added labels in case \gbc{overlaylabels}, % \gbc{truebbox}, and \gbc{clipall} are all \gbc{false}. % \begin{macrocode} %<*MP> boolean overlaylabels, underlaylabels, havebackground; overlaylabels := false; underlaylabels := false; havebackground := false; % 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> newpicture (foreground_labels); newpicture (background_labels); havebackground := false; save labelbb; pair labelbb.ll, labelbb.ur; labelbb.ll := labelbb.ur := origin; % enddef; % \end{macrocode} % % \DescribeRoutine{mfpicenv} % We define a \gbc{mfpicenv} environment for compatibility with older % \file{graphbase.mf} (mainly for \prog{fig2dev}'s \file{genmf.c}). % \DescribeRoutine{endmfpicenv} % Actually, I have no idea if \prog{fig2dev} even works with the current % \mfpic. % % \DescribeRoutine{bounds} % This used to be for compatibility also, but I decided it was a % convenient abbreviation, so \mfpic{} uses it now. % \begin{macrocode} def mfpicenv = enddef; def endmfpicenv = enddef; def bounds (expr a, b, c, d) = xneg := a; xpos := b; yneg := c; ypos := d; enddef; % \end{macrocode} % % \DescribeRoutine{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) = % beginfig (ch); % begingroup gcode := ch; save w_, h_, d_; charwd := (xpos-xneg)*xscale*unitlen; charht := (ypos-yneg)*yscale*unitlen; chardp := 0; %<*MF> charcode := if known ch: byte ch else: 0 fi; w_ := hround (charwd*hppp); h_ := vround (charht*hppp); d_ := vround (chardp*hppp); charic := 0; clearxy; clearit; clearpen; scantokens extra_beginchar; % %<*MP> w_ := charwd; h_ := charht; d_ := chardp; % 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; % >> "TFM charwd = " & decimal charwd & "pt#"; % >> "TFM charht = " & decimal charht & "pt#"; % >> "width = " & decimal w_ & "bp"; % >> "height = " & decimal h_ & "bp"; GBenddebug; fi DoClip (active_plane); if clipall: clipto (active_plane) rect (origin, (w_, h_)); fi if showbbox: noclip ( safedraw rect (origin, (w_, h_)) ); fi %<*MF> scantokens extra_endchar; if proofing > 0: makebox (proofrule); fi chardx := w_; % desired width of character in pixels shipit; if displaying > 0: makebox (screenrule); showit; fi endgroup % % \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, we just set the bounding box to the % coordinates determined by \gbc{w_} and \gbc{h_}, otherwise we expand % them to the \gbc{labelbb} values. % \begin{macrocode} elseif clipall: _ll := origin; _ur := (w_,h_); else: % expand to accomodate labels _ll := pairmin ((0, 0 ), labelbb.ll); _ur := pairmax ((w_, h_), labelbb.ur); 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 % cases. That's why we don't just let \MP{} determine the bounding box, % but force the upper and lower coordinates to differ. % \begin{macrocode} _ur := pairmax (_ur, _ll + eps*(1, 1)); setbounds active_plane to rect (_ll, _ur); % \end{macrocode} % Finally, if \gbc{overlaylabels} or \gbc{underlaylabels} was true during % a \gbc{newgblabel} command, then the label was not added to % \mfc{currentpicture} but rather to \gbc{foreground_labels} or % \gbc{background_labels}. We add those pictures now, the former on top of % \mfc{currentpicture}, the latter underneath. This might extend the bbox % calculated above, but that is one of the effects we \emph{want} to % achieve. Picture variables can consume a lot of memory, so we clear % each one after we have added it. Unfortunately, we will temporarily % have two copies of the current picture in memory for background text, so % we perform this operation only if \gbc{havebackground} is true. % \begin{macrocode} if havebackground: addto background_labels also active_plane; active_plane := background_labels; background_labels := nullpicture; fi addto active_plane also foreground_labels; foreground_labels := nullpicture; endfig; % enddef; % \end{macrocode} % % % \section{Text}\label{text} % % In the \MP{} version, \gbc{label_adjust}, \gbc{label_sep} and % \gbc{labelpath_sep} are the equivalent of \mfpic's \cs{tlabeloffset}, % \cs{tlpointsep} and \cs{tlpathsep}. In the \MF{} version they are still % needed (in \gbc{textrect}, etc.) to place the paths that are to surround % the text that \TeX{} places. % % \gbc{label_adjust} is a vector displacement applied to all labels, % while \gbc{label_sep} is the distance from the label to % the point of placement, when that point is on the edges of the label's % bounding box. Both are in device coordinates (e.g., \mfc{3bp}). % Finally, \gbc{labelpath_sep} is the separation of a surrounding path % from the text. % \begin{macrocode} pair label_adjust; label_adjust := origin; numeric label_sep, labelpath_sep ; label_sep := 0; labelpath_sep := 0; % \end{macrocode} % % Another aspect of trying to make \mfpic's \file{.mp} and \file{.mf} % the same, we here define a version of \mfc{verbatimtex} for \MF. This % works only if \mfc{etex} is followed by a semicolon, and no semicolons % appear in the \TeX{} material. (There may be other forbidden things, and % certainly any parentheses have to be in matching pairs. Not so obvious % is that \cs{begingroup} and \cs{endgroup} have to be balanced: \MF{} % sees \cs{begingroup} as `\verb$\$' plus \mfc{begingroup}.) We would % like the output of \mfpic{} under the \opt{metapost} option to be usable % in \MF{} with minimal changes. % \begin{macrocode} %def verbatimtex text t = enddef; % \end{macrocode} % % \subsection{Placement of text, \MP{} only}\label{placement} % % \DescribeRoutine{newgblabel} % 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 macro \gbc{newgblabel} takes 6 parameters. The first three % parameters could easily be condensed into two if \mfpic{} support were % all that was required, however I thought it best to make it general. % The parameters \gbc{hf} and \gbc{vf} are numeric, with \gbc{hf} % representing the fraction of the text that lies left of the point where % the text is placed and \gbc{vf} represents the fraction of % text that lies below that point. However, if the third parameter is % \mfc{true}, then \gbc{vf} is relative to the baseline (i.e., the depth % is ignored). In \mfpic{} this is only used with \gbc{vf = 0} to get % placement on the baseline. % % These three parameters correspond to the optional parameter of % \cs{tlabel} in \mfpic{} as follows: % \begin{itemize} % \item \gbc{hf} determines horizontal position: $0=\mathtt{l}$, % $.5=\mathtt{c}$, and $1 = \mathtt{r}$. % \item \gbc{vf} and \gbc{BL} determine vertical position. For placement % option \texttt{B}, $\mathtt{vf} = 0$ and \gbc{BL} is \mfc{true}. For the % rest, \gbc{BL} is \mfc{false} and \gbc{vf} corresponds as follows: % $0 = \mathtt{b}$, $.5 = \mathtt{c}$ and $1 = \mathtt{t}$. % \end{itemize} % The remaining parameters have the following meanings: % \begin{itemize} % \item \gbc{r} is degrees of rotation about the specified point. % \item \gbc{s} is a string or picture expression (typically % \mfc{btex ... etex} code) % \item \gbc{pts} is a list of pairs in graph coordinates. % \end{itemize} % First the bounding box of the picture is determined using % \gbc{pathdims}. (Why \texttt{\textit{path}dims}? Because it was written % for the paths that surround text, and was then incorporated into text % placement when \gbc{newgblabel} replace \gbc{gblabel}.) Then % \gbc{readjustdims} extends that box by \gbc{label_sep}, a new % reference point for the picture is calculated using % \DescribeRoutine{ref_shift}\gbc{ref_shift}, and then \gbc{thegblabel} % rotates it around the reference point and adds the \gbc{label_adjust}. % Finally, for each \gbc{_itm} in \gbc{pts}, the result is shifted by % \gbc{_itm}. If \gbc{overlaylabels} is true, the label is placed on the % picture \gbc{foreground_labels} and added to \gbc{active_plane} at % \gbc{endmfpic}. If \gbc{underlaylabels} is true, it is placed in % picture \gbc{background_labels} and \gbc{active_plane} is placed on top % of it. Otherwise, it is added directly to \gbc{active_plane} and the % \gbc{labelbb} variables are adjusted. % % We also use \gbc{ref_shift} in \MF{} since the curves that surround text % require it. % % \DescribeRoutine{gblabel} % We keep \gbc{gblabel} for backward compatibility with old \mfpic{} % files, but it merely calls \gbc{newgblabel}. While the old \gbc{gblabel} % had the same flexibility as \gbc{newgblabel}, this one assumes that the % parameters are only those that \mfpic{} would write. % % We provide a null definition of newgblabel for \MF{} to allow \mfpic's % \file{.mp} files to be somewhat usable with minimal changes. It % requires a text parameter, since \MF{} would be unable to evaluate % \mfc{btex} expressions. % \begin{macrocode} %<*MP> vardef newgblabel (expr hf, vf, BL, r) (expr s) (text pts) = save _lab, _ll, _ur; picture _lab; pair _ll, _ur; _lab := makepicture (s); pathdims (origin, _lab) (_ll, _ur); readjustdims (_ll, _ur) (label_sep); _lab := thegblabel (ref_shift (hf, vf, BL, _ll, _ur), r, _lab); save _b; pair _b; for _itm = pts: _b := zconv (_itm); if overlaylabels: addto foreground_labels also _lab shifted _b _wc_ tlabelcolor; elseif underlaylabels: addto background_labels also _lab shifted _b _wc_ tlabelcolor; havebackground := true; else: addto active_plane also _lab shifted _b _wc_ tlabelcolor; labelbb.ll := pairmin (_b + llcorner _lab, labelbb.ll); labelbb.ur := pairmax (_b + urcorner _lab, labelbb.ur); fi endfor % %vardef newgblabel (expr hf, vf, BL, r) (text s) (text pts) = enddef; % Assumes a+b=1 and either c+d=1 or c=d=0: %vardef gblabel (expr a, b, c, d, r) (expr s) (text t) = %vardef gblabel (expr a, b, c, d, r) (text s) (text t) = newgblabel (b, d, (c = 0) and (d = 0), r) (s) (t); enddef; 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, \MF{} or \MP{}}\label{decorating} % % 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 % \DescribeRoutine{textrect}\gbc{textrect}, the path is a rectangle with % optionally rounded corners. The second parameter, \gbc{rad}, is the % radius of quarter circles at the corners (in device units). In the other % two cases, the path is an ellipse. They differ in the meaning of the % second parameter. % % In \DescribeRoutine{textoval}\gbc{textoval}, the second parameter % \emph{multiplies} the ratio of width to height of the text to produce % the ratio for the ellipse. Thus, with \gbc{mult}=1, the ratio will be % the same as that of the text. In % \DescribeRoutine{textellipse}\gbc{textellipse}, the second parameter % \gbc{rat} is the actual value of the ratio of width to height of the % ellipse and a value of 1 produces a circle. In either macro, if that % parameter is 0, we draw a rectangle. % % The size of each path is determined so that, when the text is placed and % the path drawn, it passes through the four corners of the following % rectangle: the rectangle which just encloses the text plus the amount of % space on all sides determined by \gbc{labelpath_sep}. Note that this means % a rectangle with rounded corners will have larger height and width than % one without. These versions always center the surrounding path on the % the point \gbc{loc}. The extended versions (below) have the same % flexibility of placement as the commands that place the label being % surrounded. % % The first parameter \gbc{lbl} is either a pair representing the % height and width of the text (only possibility in \MF) or the actual % 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}\gbc{textrectx} is the extended version of % \gbc{textrect} which allows the same adjustments to the rectangle that we % can apply to the text it surrounds (via \gbc{newgblabel}). In fact, it % calculates the position in exactly the same manner as that macro, and % the first 4 parameters encode that position in the same way. % % The placement of each path is: shifted and rotated by the same amount % as the text (by \gbc{ref_shift}) according to the first four parameters, % then shifted to the point given in the third parameter \gbc{loc}, and % finally shifted by the vector specified in \gbc{label_adjust}. % % \gbc{lbl} is either the upper right corner of the text or the label % itself. In the first case \gbc{xy} is the lower left corner, in the % second case it is a dummy parameter, the bounding box being obtained (in % \gbc{pathdims}) by measuring the label. For these extended macros, the % parameters \gbc{lbl}, \gbc{mult}, \gbc{rad}, and \gbc{loc} are as in % the unextended versions. % % \DescribeVariable{roundends}\gbc{roundends} is a boolean. We really only % need it to be a type distinguishable from any numeric value. \Mfpic{} % users can specify it rather than an explicit radius, and when the code % of \gbc{textrectx} detects this, it uses the maximum radius for the % corners (making the short side of the `rectangle' a semicircle). That % is, if \gbc{rad} is a boolean (and \mfc{true}) then the radius at the % corners is so chosen. If \gbc{rad} is \mfc{false} the corners are not % rounded at all. % \begin{macrocode} boolean roundends; roundends := true; vardef textrectx (expr a, b, c, rot, xy, lbl, rad, loc) = save ll, ur, _r, f, zz; pair ll, ur, zz; path f; pathdims (xy, lbl) (ll, ur); readjustdims (ll, ur) (labelpath_sep) _r := if numeric rad: rad elseif not boolean rad: 0 elseif rad: emin (xpart(ur-ll), ypart (ur-ll))/sqrt(2) else: 0 fi; if _r = 0: f := rect (ll, ur); else: save p, q; pair p[]; path q; p1 := ur - _r*dir(45); p3 := ll + _r*dir(45); p2 := (xpart p3, ypart p1); p4 := (xpart p1, ypart p3); % \end{macrocode} % We allow the rounding radius to be negative and make the corners % indented in that case. We no longer reverse the path in this case. % \begin{macrocode} q := if _r < 0: reverse fi quartercircle scaled 2_r; f := (q shifted p1)--(q rotated 90 shifted p2) --(q rotated 180 shifted p3) --(q rotated -90 shifted p4)--cycle; fi readjustdims (ll, ur) (label_sep - labelpath_sep); invvconv (thegblabel (ref_shift(a, b, c, ll, ur), rot, f)) shifted loc enddef; % \end{macrocode} % % \DescribeRoutine{textellipsex}The macro \gbc{textellipsex} is a simlar % extension for \gbc{textellipse}. It and the related macro % \DescribeRoutine{textovalx}\gbc{textovalx} now call a common macro with % different values of a boolean parameter. % \begin{macrocode} def textovalx = xellipse (true) enddef; def textellipsex = xellipse (false) enddef; % \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*ww/hh}, 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; pathdims (xy, lbl) (ll, ur); readjustdims (ll, ur) (labelpath_sep) cc := .5[ll, ur]; (ww, hh) = ur - cc; if (ww = 0) or (hh = 0): f = (ll--ur); else: save aa, bb; % \end{macrocode} % % The \gbc{aa} and \gbc{bb} are now calculated in a way that decreases the % chance of overflow. As a side effect, negative \gbc{mult} no longer % reverses the path. % \begin{macrocode} aa := ww ++ if aspect: ww else: hh fi *mult; bb := hh ++ if aspect: hh else: ww fi /mult; f := ellipse (cc, aa, bb, 0); fi readjustdims (ll, ur) (label_sep - labelpath_sep); invvconv (thegblabel (ref_shift(a, b, c, ll, ur), r, f)) shifted loc fi enddef; % \end{macrocode} % % \DescribeRoutine{pathdims} % This has been changed to make the code of \mfpic{} a bit simpler and % to aid in backward compatibility. It takes a couple of pairs (the actual % or nominal label bounding box corners) or something visible (picture, % string or path) and assigns suitable values to \gbc{ll} and \gbc{ur}. % % \DescribeRoutine{readjustdims} % This is used to add the separations needed to implement the effects of % \gbc{label_sep} and \gbc{labelpath_sep}. % \begin{macrocode} def pathdims (expr xy, lbl) (suffix ll, ur) = if pair lbl: ll := xy; ur := lbl; else: % ll := ur := origin; %<*MP> setpicture (_lbl) makepicture (lbl); ll := llcorner _lbl; ur := urcorner _lbl; % fi enddef; def readjustdims (suffix ll, ur) (expr s) = ll := ll - s*(1,1); ur := ur + s*(1,1); enddef; % \end{macrocode} % % % \section{Additional Functions}\label{functions} % % 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. % % 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 the smallest number whose reciprocal is a % usable number. (\mfc{epsilon} is the smallest possible positive number % in \MF.) % % The value \gbc{eps/2 + epsilon} is the smallest value with % reciprocal less than \mfc{infinity}. I set \gbc{nottoosmall} a speck % bigger to ensure that the same is true of \gbc{2*(nottoosmall/2)}. % % We set \gbc{secd x = 1/(cosd x)} unless \gbc{cosd x} is less than % \gbc{reallysmall}, then we set it equal to \gbc{1/reallysmall}. We do a % similar thing with \gbc{cscd}. (When such a substitution happens % \DescribeRoutine{TruncateWarn}\gbc{TruncateWarn} prints a message % that a truncation has taken place.) % % Why not just determine what number will produce arithmetic overflow and % test for that? Because I'm lazy: it would require a different number % for each of the functions. Instead, since \MF{} has no `arithmetic % underflow', I compute something that is guaranteed to work and occurs % 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 reallysmall; reallysmall := 3epsilon; newinternal nottoosmall; nottoosmall := eps/2 + 2epsilon; def signof (expr X) = if X < 0: - fi enddef; def TruncateWarn expr s = GBwarn s & " is too large or undefined, so it will be truncated."; enddef; % \end{macrocode} % In addition to \mfc{sind} and \mfc{cosd} which take angles in degrees, % we define the remaining trig functions \gbc{tand}, \gbc{cotd}, % \gbc{secd}, and \gbc{cscd}. % % We define \RoutineIndex{secd}\gbc{secd}, one of the simplest, to include % an out of range test (which also prevents division by 0). Then % \RoutineIndex{tand}\gbc{tand} can make use of it without any division. % We do the same with \RoutineIndex{cscd}\gbc{cscd} and % \RoutineIndex{cotd}\gbc{cotd}. % \begin{macrocode} vardef secd primary X = setnumeric (temp) cosd(X); if abs(temp) < reallysmall: TruncateWarn "Secant or Tangent"; temp := signof (temp) reallysmall; fi 1/temp enddef; vardef tand primary X = sind(X)*secd(X) enddef; vardef cscd primary X = setnumeric (temp) sind(X); if abs(temp) < reallysmall: TruncateWarn "Cosecant or Cotangent"; temp := signof(temp) reallysmall; fi 1/temp enddef; vardef cotd primary X = cosd(X)*cscd(X) enddef; % \end{macrocode} % These are the inverse functions, which return an angle in degrees: % \RoutineIndex{acos}\gbc{acos}, \RoutineIndex{asin}\gbc{asin} and % \RoutineIndex{atan}\gbc{atan}. % \begin{macrocode} vardef acos primary X = if abs X > 1: TruncateWarn "Argument of arccosine"; angle (signof(X) 1, 0) else: angle (X, 1 +-+ X) fi enddef; vardef asin primary X = if abs X > 1: TruncateWarn "Argument of arcsine"; angle (0, signof(X) 1) else: angle (1 +-+ X, X) fi enddef; vardef atan primary X = angle (1, X) enddef; % \end{macrocode} % Now the trig functions that take angles in radians: % \RoutineIndex{sin}\gbc{sin}, \RoutineIndex{cos}\gbc{cos}, % \RoutineIndex{tan}\gbc{tan}, \RoutineIndex{cot}\gbc{cot}, % \RoutineIndex{sec}\gbc{sec} and \RoutineIndex{csc}\gbc{csc}. % \begin{macrocode} vardef sin primary X = sind (X*radian) enddef; vardef cos primary X = cosd (X*radian) enddef; 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} % % It is useful to have a \DescribeRoutine{degrees}command to convert % from radians to degrees and one to \DescribeRoutine{radians}convert % from degrees to radians. Thus \gbc{degrees(pi)} produces % (approximately) $180$ and \gbc{radians(180)} is approximately $\pi$. % \begin{macrocode} vardef degrees (expr t) = t*radian enddef; vardef radians (expr t) = t/radian enddef; % \end{macrocode} % % And the inverses (\RoutineIndex{invsin}\gbc{invsin}, % \RoutineIndex{invcos}\gbc{invcos} and \RoutineIndex{invtan}\gbc{invtan}) % that return angles in radians. % \begin{macrocode} vardef invcos primary X = (acos X)/radian enddef; vardef invsin primary X = (asin X)/radian enddef; vardef invtan primary X = (atan X)/radian enddef; % \end{macrocode} % % Here we define the standard exponential function % \RoutineIndex{exp}\gbc{exp}. (The \MF{} function \mfc{mexp} has the % unusual base $e^{1/256}$ to avoid overflow.) The inverse of \gbc{exp} is % the natural logarithm (\RoutineIndex{ln}\gbc{ln} or % \RoutineIndex{log}\gbc{log}). We also have the general base logarithm % \RoutineIndex{logbase}\gbc{logbase} and its two special instances % \RoutineIndex{logtwo} \gbc{logtwo} and \RoutineIndex{logten}\gbc{logten}. % \begin{macrocode} vardef exp primary X = mexp (256 * X) enddef; vardef ln primary X = (mlog X) / 256 enddef; def log = ln enddef; vardef logbase (expr B) primary X = (mlog X)/(mlog B) enddef; def logtwo = logbase( 2) enddef; def logten = logbase(10) enddef; % \end{macrocode} % \CMF's pair variables are a decent replacement for complex variables. % These give some of the more basic functions of standard complex % analysis: \RoutineIndex{Arg}\gbc{Arg}, \RoutineIndex{Log}\gbc{Log}, % \RoutineIndex{cis}\gbc{cis}, \RoutineIndex{zexp}\gbc{zexp} and % \RoutineIndex{sgn}\gbc{sgn}. % \begin{macrocode} vardef Arg primary Z = (angle Z)/radian enddef; vardef Log primary Z = (ln (abs Z), Arg Z) enddef; vardef cis primary T = dir (T*radian) enddef; vardef zexp primary Z = (exp (xpart Z)) * cis (ypart Z) enddef; vardef sgn primary Z = if not (Z = origin): unitvector fi Z enddef; % \end{macrocode} % % The hyperbolic functions: \RoutineIndex{cosh}\gbc{cosh} % \RoutineIndex{sinh}\gbc{sinh}, \RoutineIndex{tanh}\gbc{tanh}, % \RoutineIndex{sech}\gbc{sech}, \RoutineIndex{csch}\gbc{csch} and % \RoutineIndex{coth}\gbc{coth}. % \begin{macrocode} vardef cosh primary X = setnumeric (temp) 2 exp (-abs(X)); if temp < reallysmall: TruncateWarn "Cosh"; temp := reallysmall; fi 1/temp + temp/4 enddef; vardef sinh primary X = setnumeric (temp) 2 exp (-abs(X)); if temp < reallysmall: TruncateWarn "Sinh"; temp := reallysmall; fi signof (X) (1/temp - temp/4) enddef; vardef sech primary X = setnumeric (temp) exp(-(abs (X))); 2temp/(1 + temp*temp) enddef; vardef tanh primary X = setnumeric (temp) exp(-2(abs (X))); signof (X) (1 - temp)/(1 + temp) enddef; vardef csch primary X = save temp, tempa; temp := exp(-(abs (X))); tempa := (1 - temp*temp)/2; if tempa < reallysmall: TruncateWarn "Csch"; tempa := reallysmall; fi signof (X) temp / tempa enddef; vardef coth primary X = setnumeric (temp) tanh(X); if abs(temp) < reallysmall: TruncateWarn "Coth"; temp := signof (X) reallysmall; fi 1/temp enddef; % \end{macrocode} % The inverses of some of the hyperbolic functions: % \RoutineIndex{acosh}\gbc{acosh}, \RoutineIndex{asinh}\gbc{asinh} and % \RoutineIndex{atanh}\gbc{atanh}. % \begin{macrocode} vardef acosh primary y = if y < 1: TruncateWarn "acosh"; 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: TruncateWarn "atanh"; signof (y) infinity fi enddef; % \end{macrocode} % % \DescribeRoutine{polar} % \gbc{polar} converts a polar coordinate pair $(r, \theta)$ to the % corresponding rectangular coordinate pair. % \DescribeRoutine{id} % \gbc{id} returns its argument, which can be any expression of any type. % \begin{macrocode} vardef polar primary p = (xpart p) * dir (ypart p) enddef; def id (expr x) = x enddef; % \end{macrocode} % % The definition of powers (\gbc{x**y}) in \prog{plain} \MF{} and \MP{} % could be more accurate. In particular \gbc{x=2**10} ought to be an % integer (that is, satisfy \mfc{x=floor x}). Here we redefine % \prog{plain}'s \mfc{**}, intercepting the case of a positive integer % power of an integer. % \begin{macrocode} primarydef x**y = if y=2: x*x elseif (x = floor x) and (abs y = floor y): 1 for n=1 upto y: *x endfor else: takepower y of x fi enddef; let ^ = **; % \end{macrocode} % % \section{Coordinate Systems and Transformations}\label{systems} % % \DescribeVariable{T_stack} % We want to define a localization of the current transform. To do % this we define a LIFO stack of transforms \gbc{T_stack[\,]}, and a pair of % macros. % \DescribeRoutine{T_push} % \gbc{T_push} puts its argument (a transform) on the stack, and % \DescribeRoutine{T_pop} % \gbc{T_pop} pops it off into its argument (a transform variable name). % We also define two localizing macros % \DescribeRoutine{bcoords}\gbc{bcoords} that pushes our \gbc{ztr} % on the stack, and \DescribeRoutine{ecoords}\gbc{ecoords} that pops it % off. We no longer put \gbc{vtr} on the stack, since we can recalculate % it whenever \gbc{ztr} is changed. \gbc{apply_t} always did this, now % \gbc{ecoords} does so as well. % \begin{macrocode} transform T_stack[]; numeric T_stack; T_stack := 0; def T_push (expr T) = T_stack[incr T_stack] := T; enddef; def T_pop (suffix $) = if T_stack > 0: $ := T_stack[T_stack]; T_stack := T_stack - 1; fi enddef; def bcoords = hide ( T_push (ztr) ) enddef; def ecoords = hide ( T_pop (ztr); vtr := vectorpart 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 a % phrase which, were it to follow a path, would produce a transformed % path. Knuth calls such a phrase a \emph{transformer}. % \begin{macrocode} vardef vectorpart primary T = T shifted -(origin transformed T) enddef; def apply_t (text Transformer) = ztr := identity Transformer transformed ztr; vtr := vectorpart ztr; enddef; % \end{macrocode} % % And now we define some available transformers. % \RoutineIndex{xslant}\gbc{xslant}, \RoutineIndex{yslant}\gbc{yslant}, % \RoutineIndex{zslant}\gbc{zslant}, \RoutineIndex{xyswap}\gbc{xyswap} and % \RoutineIndex{boost}\gbc{boost}. The only two that need comment are % \gbc{zslant} and \gbc{boost}. I know that boost comes from special % relativity, but I have no idea why zslant is a `slant'. % \begin{macrocode} def xslant = slanted enddef; % (x+sy, y). def 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 and return a path in graph coordinates. % For the most part they are named and defined to apply a similarly named % transform to the path and return the result. There are two exceptions. % When we draw things, we expect that rotated and reflected objects appear % congruent to the originals. If we define a path in graph coordinates, % and the $x$ and $y$ directions are scaled differently, then simply % rotating the graph coordinates will distort angles. The same is true of % reflection. Therefore, we apply \gbc{vtr} (so we are in drawing % coordinates) then rotate or reflect, then apply \gbc{inverse vtr}. This % may be a mistake, or perhaps we should do it for all of these. For now, % I'm sticking with the scheme I inherited. One can always use % \gbc{coords} and \gbc{apply_t} if one wants the difference in scales % ignored. % % \DescribeRoutine{transformedpath} % This is a vardef that reads an undelimited path expression and returns % the path transformed by the text argument. All the others run this, % allowing it to grab the path expression. % % \DescribeRoutine{rotatedpath} % This returns the path rotated around point \gbc{p} by angle % \gbc{th} in degrees. % % \DescribeRoutine{reflectedpath} % This reflects the path through the line containing points \gbc{p} and % \gbc{q}. % % \DescribeRoutine{scaledpath} % This returns the path scaled so that distances from the point % \gbc{p} are multiplied by \gbc{s}. % \DescribeRoutine{xscaledpath} % \gbc{xscaledpath} is similar, but only the horizontal distances from % the line $x={}$\gbc{a} are multiplied by \gbc{s}. And with % \DescribeRoutine{yscaledpath}\gbc{yscaledpath} the vertical distances % from the line $y={}$\gbc{b} are multiplied by \gbc{s}. % % \DescribeRoutine{xslantedpath} % This returns the path xslanted with line $y = {}$\gbc{b} % being the pivot rather than the $x$-axis. \DescribeRoutine{slantedpath} % The command \gbc{slantedpath} is just an alias for \gbc{xslantedpath}, % while \DescribeRoutine{yslantedpath}\gbc{yslantedpath} is the vertical % version, yslanted with line $x = {}$\gbc{a} being the pivot rather than % the $y$-axis. % % \DescribeRoutine{shiftedpath} % This returns the path shifted by the vector (pair) \gbc{v}. % % \DescribeRoutine{xyswappedpath} % This returns the path in which all points have had the coordinates % exchanged $(a, b) \to (b, a)$. Note that this is not the same as % \gbc{reflectedpath ((0,0), (1,1))}, as it performs the reflection in % graph coordinates, as its name implies. If \gbc{vtr} has not been % changed (by \gbc{apply_t}) then \gbc{xyswappedpath} will convert % vertical lines to horizontal and vice versa. The \gbc{reflectedpath} % version will not when $x$ and $y$ are scaled differently, for then the % line \gbc{(0,0)--(1,1)} is not at a 45 degree angle in device % coordinates where drawing takes place. % % \begin{macrocode} vardef transformedpath (text Transformer) expr f = f Transformer enddef; def rotatedpath (expr p, th) = transformedpath ( transformed vtr rotatedaround (p transformed vtr, th) transformed (inverse vtr) ) enddef; def reflectedpath (expr p, q) = transformedpath ( transformed vtr reflectedabout (p transformed vtr, q transformed vtr) transformed (inverse vtr) ) enddef; def scaledpath (expr p, s) = transformedpath (shifted -p scaled s shifted p) enddef; def xscaledpath (expr a, s) = transformedpath (shifted (-a, 0) xscaled s shifted (a, 0)) enddef; def yscaledpath (expr b, s) = transformedpath (shifted (0, -b) yscaled s shifted (0, b)) enddef; def slantedpath = xslantedpath enddef; def xslantedpath (expr b, s) = transformedpath (shifted (0, -b) slanted s shifted (0, b)) enddef; def yslantedpath (expr a, s) = transformedpath (shifted (-a, 0) yslant s shifted (0, a)) enddef; def shiftedpath (expr v) = transformedpath (shifted v) enddef; def xyswappedpath = transformedpath (xyswap) enddef; % \end{macrocode} % % 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. % % \DescribeRoutine{partialpath} % The \gbc{partialpath} macro takes two fractions $\alpha$ and $\beta$ % between 0 and 1, and a path \gbc{f}, and returns the subpath from % $\alpha * {} $\meta{length of \gbc{f}} to $\beta * {}$\meta{length of % \gbc{f}} of \gbc{f}. Since the \gbc{gettime} routine was written to % find the times for an increasing sequence of lengths, it was optimized % to save the index of the previous length and begin from there. Thus it % is more efficient to find the smaller of \gbc{a} and \gbc{b} first. % % Since running \gbc{gettime} would be a very inefficient way to get the % first or last point of a path we skip that if either fraction is $0$ or % $1$ (a common use is to get the first or last half of a path). We also % skip finding the second time if \gbc{a = b} (an unlikely choice, but % legal). % % \DescribeRoutine{gsubpath} % \gbc{gsubpath} is the same as \MF's subpath primitive, but follows the % prefix macro syntax of accepting a path expression (rather than a % primary) and wrapping the result in a \mfc{vardef}. % \begin{macrocode} vardef partialpath (expr a, b) expr f = save flag, flo, fhi, lo, hi, n; boolean flag; flag = true; convertpath (g) f; n := length f; flo := snapto emin(a,b); if flo = 0: lo := 0; elseif flo < 1: setuplengtharray (cum, tot, idx) g; flag := false; lo := gettime (cum, idx) (flo*tot); else: lo := n; fi fhi := snapto emax (a,b); if flo = fhi: hi := lo; elseif fhi < 1: if flag: setuplengtharray (cum, tot, idx) g; fi hi := gettime (cum, idx) (fhi*tot); else: hi := n; fi if a > b: reverse fi subpath (lo, hi) of f enddef; vardef gsubpath (expr a, b) expr f = subpath (a, b) of f enddef; % \end{macrocode} % % \DescribeRoutine{setuplengtharray} % This does the frequently repeated saving, rescaling and initializing % for those commands that need to convert distance along a path to the % corresponding time or point. A path variable should follow, but that % is picked up by the \gbc{makelengtharry} at the end. % \begin{macrocode} def setuplengtharray (suffix cum, tot, idx) = save cum, tot, idx; idx := 0; tot := makelengtharray (cum) enddef; % \end{macrocode} % % \DescribeRoutine{pathtime} % \gbc{pathtime} returns the time \mfc{t} such that \mfc{point t of p} is % \gbc{frac} of the distance along \gbc{p} from the start, and % \DescribeRoutine{pathpoint}\gbc{pathpoint} returns the point itself. % Because the \gbc{gettime} routine requires it anyway, we truncate % \gbc{frac} to the interval $[0,1]$ and avoid calling that rather % lengthy function at $0$ and $1$. % % The path in pathtime should be in device coordinates, whereas the % user-level command \gbc{pathpoint} expects it in graph coordinates. % In fact, since \gbc{pathpoint} would most likely be used in the % argument of some figure macro in \mfpic{}, it would require a % previously stored path, so we make the path a suffix parameter. % \begin{macrocode} vardef pathtime@# (suffix p) = if @# <= 0: 0 elseif @# >= 1: length p else: setuplengtharray (cum, tot, idx) p; gettime (cum, idx) (@#*tot) fi enddef; vardef pathpoint (expr frac) (suffix p) = convertpath (_pp) p; pnt[pathtime[frac] (_pp)] (p) enddef; % \end{macrocode} % % % \section{Picture-level Operations}\label{picture} % % \subsection{Bitwise logical operations}\label{logical} % % None of these operations are available in \MP. Mostly these are used by % higher level operations. Those higher level operations are available in % \MP, but need to be defined differently. % % 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 only in \gbc{shadepic}. They are all % rather wasteful of memory. % % \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. It is important to note that we can apply % \gbc{mono} only to the suffix parameter in such things as \gbc{orto}. % The expression parameter needs to be prepared by the routine that calls % these. The return result is culled, so it consists only of 0s and 1s. % \begin{macrocode} %<*MF> def mono (suffix u) = cull u keeping (1, infinity); enddef; % \end{macrocode} % \DescribeRoutine{andto, picand} % The bitwise and: in the resulting picture, a pixel is \emph{on} if and % only if it is \emph{on} in both \gbc{u} and \gbc{v}. \gbc{andto} is % only used in \gbc{interior} and \gbc{interiors}, \gbc{picand} is not % used at all. % \begin{macrocode} def andto (suffix u) (expr v) = mono (u); addto u also v; cull u keeping (2, 2); enddef; primarydef u picand v = begingroup setpicture (t) u; andto (t, v); t endgroup enddef; % \end{macrocode} % \DescribeRoutine{orto, picor} % The inclusive or: in the result, a pixel is \emph{on} if and only if it % is \emph{on} in \gbc{u} or \gbc{v} or both. I've written these so that % it doesn't matter if the expression parameter is not mono. It % \emph{is} required that it have only positive pixels. The command % \gbc{orto} is only used three places: in \gbc{coloraddto}, which % is itself never used, and in \gbc{patcharcs} and \gbc{patchrays}. This % lack of use is because a less memory intensive version, \gbc{_orto}, is % defined later, and that is what we use. Usually we build a picture % in a variable \gbc{src} and add that onto another variable \gbc{dest}. % If one used \gbc{orto (dest, src)}, then \MF{} would evaluate \gbc{src} % and pass a \emph{copy} of it as the parameter of \gbc{orto}. This % doubles the memory used, so mostly we use \gbc{_orto}, which passes both % parameters as suffixes. \gbc{picor} is never used. % \begin{macrocode} def orto (suffix u) (expr v) = mono (u); addto u also v; cull u keeping (1, infinity); enddef; primarydef u picor v = begingroup setpicture (t) u; orto (t, v); t endgroup enddef; % \end{macrocode} % \DescribeRoutine{xorto, picxor} % The exclusive or, also called the symmetric difference: % in the result, a pixel is \emph{on} if and only if it is \emph{on} in % \gbc{u} or \gbc{v}, but not both. These are not used elsewhere in % \grafbase. % \begin{macrocode} def xorto (suffix u) (expr v) = mono (u); addto u also v; cull u keeping (1, 1); enddef; primarydef u picxor v = begingroup setpicture (t) u; xorto (t, v); t endgroup enddef; % \end{macrocode} % \DescribeRoutine{subto} % 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}. It is % unclear whether a \gbc{v} with negative weights will ever occur, but % if so, subtracting negative pixels ought to be like adding positive % ones, so I've changed \mfc{keeping (1,1)} to \gbc{keeping (1,infinity)}. % With this understanding, it doesn't matter here whether \gbc{v} is % not mono. As with \gbc{orto}, we have a more memory efficient % \gbc{_subto} and now use that everywhere. \gbc{subto} is only used in % \gbc{coloraddto}, which is not used anymore. The binop version % \DescribeRoutine{picsub}\gbc{picsub} is used only in \gbc{shadepic}. % \begin{macrocode} def subto (suffix u) (expr v) = mono (u); addto u also -v; cull u keeping (1, infinity); enddef; primarydef u picsub v = begingroup setpicture (t) u; mono (t); subto (t, v); t endgroup enddef; % % \end{macrocode} % % \subsection{Producing and modifying pictures}\label{pictures} % % Here we define some slightly higher level commands that make use (in \MF) % of the previous bitmap operations. In \MP, they mostly need different % 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 was once a useful abbreviation. In \MF{} it adds when the color % is not white, subtracts when it is. Grays are handles in \MF{} by % appropriate preparation of \gbc{u} and \gbc{v}. See, for example, the % code of \gbc{colorsafefill}. In \MP{} it is an abbreviation for the % basic \mfc{addto} operation. It was defined only so that \MP{} and \MF{} % can share the same higher level code. % % When the last parameter \gbc{v} is the name of picture we can save % memory if we pass the name rather than the value. Problems with picture % memory turned up in the shading macros for \MF{} and the dashing macros % for \MP{}. \DescribeRoutine{coloraddon}\gbc{coloraddon} applies this % memory-saving trick and has completely replaced \gbc{coloraddto} in % \grafbase{} code. Since \gbc{coloraddto} turned out to be used only with % \gbc{u} equal to \gbc{active_plane}, we eliminate that parameter from % \gbc{coloraddon}. % % \DescribeRoutine{_orto} % This version of \gbc{orto} saves memory by passing \emph{both} % parameters by name. This also allows the application of \gbc{mono} to % both parameters. In addition to \gbc{coloraddon}, it is used in % \gbc{shade} and \gbc{tess}. \DescribeRoutine{_subto}We also have % \gbc{_subto}, an analogous version of \gbc{subto}. % \begin{macrocode} def coloraddto (expr clr) (suffix u) (expr v) = %<*MF> if clr < white: orto (u, v); else: subto (u, v); fi; % % addto u also v _wc_ clr; enddef; %def orto (suffix u) (expr v) = addto u also v; enddef; % def coloraddon (expr clr) (suffix v) = %<*MF> if clr < white: _orto (active_plane, v); else: _subto (active_plane, v); fi; % % addto active_plane also v _wc_ clr; enddef; def _orto (suffix u, v) = % mono (u); mono (v); addto u also v; %cull u keeping (1, 2); enddef; %<*MF> def _subto (suffix u, v) = mono (u); mono (v); addto u also -v; cull u keeping (1, 1); enddef; % % \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 with \mfpic{} version 0.7), since the % higher level commands now implement the coloring operations. % \begin{macrocode} vardef interior expr c = newpicture (v); addto v contour (c.t_); % 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 = newpicture (_ints); for _idx = 1 upto cc: addto _ints also interior cc[_idx]); endfor % 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 = if path c: % andto (vt, interior c); % clip vt to c; fi enddef; def clipsto (suffix vt, cc) = % 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 % 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. This is not used elsewhere in \grafbase{} nor \mfpic. % % Having found out that \mfc{clipped} is a \MP{} primitive, I've % changed the name to the uppercase version. % \begin{macrocode} vardef Clipped (suffix vt) expr c = setpicture (_Cl) vt; clipto (_Cl) c; _Cl enddef; %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 colored \gbc{fillcolor}. This % is not used elsewhere in \file{grafbase.mp} so it may not be really % important whether \gbc{fillcolor} and \mfc{background} are the right % choices. % \begin{macrocode} vardef picneg (suffix vt) expr c = %<*MF> setpicture (_pn) interior c; _subto (_pn, vt); % %<*MP> setpicture (_cl) vt; clip _cl to c; newpicture (_pn); addto _pn also (interior c ) _wc_ fillcolor; addto _pn also _cl _wc_ background; % _pn 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; %minpenwd := 1; % 1 pixel %minpenwd := .05bp; % 1 pixel at 1440dpi vardef picpath expr d = newpicture (v); if penwd >= minpenwd: shpath (v, drawpen) (d); % 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 % (w shifted p); % (w shifted goodpair (p)); 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), \gbc{polkadot} and % some grid-making commands. % \begin{macrocode} vardef setdot (expr apath, sc) = if cycle apath: interior else: picpath fi % (apath scaled emax (ceiling (sc), minpenwd)) % (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 rectangles $k\times (k+1)$-pixels % (assuming the aspect ratio is 1). This produces twice the number of % gray levels. In my 360dpi example we get 15 gray levels. The two % farthest apart (4 by 4 dots versus 3 by 4 dots) differ by 1/8 in % fraction of area of coverage (which we equate to grayness). % % Why can't we have 64 grey levels in a $8\times 8$ square? Clearly we % can in principle turn on any number of the 64 pixels. Unfortunately, % spread out patterns (which look best) tend to consume memory, while % clumpy patterns are hard to make good-looking. Compensating for aspect % ratios unequal to 1 is also pretty hard to do automatically. % % The parameter \gbc{dims} needs to be a pair variable, and it will be % assigned the actual dimensions of the picture returned. These routines % are complicated by the fact that we may have an aspect ratio unequal to % $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. % % The calculations assume a gray level greater than $1/2$, so the final % picture will be mostly white (for darker grays, we use the % complementary gray level to construct the `reverse video', and then % reverse back). Under this assumption, we concentrate all the black % pixels into the lower left and upper right quadrant of the picture we % are creating, so most of the calculation determines one of these % quadrants. The scratch variables \gbc{_hp} and \gbc{_vp} give the number % of horizontal and vertical pixels in the lower left quadrant, % \gbc{_dotwd} and \gbc{_dotht} do the same for the actual dot. Then % \gbc{_shp} is first set equal to one dot; then a copy of itself is added % in the upper right quadrant. Finally, the suffix parameter \gbc{dims} is % equated to the nominal width and height of the picture, and either % \gbc{_shp} or its reverse is returned. % \begin{macrocode} %<*MF> numeric shadepicsize; shadepicsize := 0.8bp; vardef shadepic (suffix dims) (expr grparam) = pair dims; setnumeric (_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_ ); newpicture (_shp); addto _shp contour rect (origin, (_dotwd, _dotht)); picdot (_shp, _shp, dims); dims := 2dims; mono (_shp); if grparam >= .5: _shp else: (interior (rect (origin, dims))) picsub _shp fi enddef; % % \end{macrocode} % % \DescribeRoutine{shaded} % This fills the interior of a contour (device coordinates) with copies of % \gbc{shadepic}. The routine \gbc{fillwith} is defined later, but its % name reflects its effect: a bounding rectangle (corners at \gbc{ll} and % \gbc{ur}) is filled with copies of a picture (in this case, the result % of \gbc{shadepic}), the picture having nominal dimensions \gbc{shdims} % in this case. % % It may seem odd that black and white return the same thing. That is % because white is handled in the calling routine by subtracting the % result. % % The \gbc{setbbox} command was defined earlier, in % section~\ref{utilities}. The bounding rectangle it obtains is only % approximate in \MF{}, but that is sufficient, since we only use it to % produce things that are eventually clipped. % % We return \gbc{picpath} for non-cycles because I once thought to make % \gbc{shaded} a replacement for \gbc{setdot} to get gray dots (in the % \gbc{polkadot} routine). That turns out not to work, but this sort of % thing is also done in most of the rendering commands that require a % closed path. % \begin{macrocode} vardef shaded (expr clr) expr c = if cycle c: %<*MP> newpicture (v); addto v contour c _wc_ clr; v % %<*MF> if (clr <= black) or (clr >= white): interior c else: save shdims, shpic; picture shpic; pair shdims; shpic := shadepic (shdims) (clr); setbbox (ll, ur) c; newpicture (vsh); fillwith (vsh) (shpic, shdims, ll, ur); clipto (vsh) c; vsh fi % else: picpath c % should we? or just make it null? fi enddef; % \end{macrocode} % % \DescribeRoutine{fillwith} % This is one of the ways we obtain something other than a solid fill. The % routines \gbc{polkadot}, \gbc{tess} and (in \MF) \gbc{shade} and % \gbc{shaded} all use it. % % It takes a picture expression \gbc{pic}, along with its dimensions (the % pair \gbc{dims}) in device coordinates, plus the opposite corners, % \gbc{ll} and \gbc{ur}, of a boundingbox rectangle, and draws that % rectangle filled with copies of \gbc{pic}. Starting with \mfpic{} % version 0.8, it adds to a predefined picture passed by name. Thus the % calling routine must make sure that picture is initialized (it need % not be \mfc{nullpicture}). % % One might do this with two nested loops, but it turns out to be much % faster (surprisingly much!) to do two separate loops: the second one % stacking copies of the row built by the first loop. % % We try to do any rounding that might have been forgotten. This code % takes a mode's aspect ratio into account so that (most) calling routines % don't have to. (That is, \gbc{dims} should be measured in horizontal % pixels, while \gbc{fwdims} is in actual pixels. This could have been % written in terms of \gbc{picdot}, which already handles aspect, but it % has got to be more efficient to do the aspect ratio calculations once % rather than every time through the loop.) % \begin{macrocode} vardef fillwith (suffix v) (expr pic, dims, ll, ur) = newpicture (b); %<*MF> save fwdims, _ll, _ur; pair fwdims, _ll, _ur; fwdims := goodpair (dims); _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); % %<*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 % 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} def thatchf (suffix v) (expr CT, sp, a, b) = begingroup setnumeric (_sp) signof (ypart b - ypart a) abs(sp); for _y = _sp*( ceiling ((ypart a)/_sp) ) step _sp until ypart b: shpath (v, hatchpen) ( ( (xpart a, _y)--(xpart b, _y) ) transformed CT ); endfor % mono (v); endgroup enddef; % \end{macrocode} % % \subsection{Tiles}\label{tiles} % % Tesselations are a type of fill in which a rectangular pattern is % repeated throughout a region. The repeated rectangle is called a tile. % We provide here an environment in which the drawing commands add to a % picture variable other than \mfc{currentpicture}. We do this very simply % by redefining \gbc{active_plane}, localizing the redefinition between % \gbc{tile} and \gbc{endtile} % % \DescribeRoutine{tile} % The macro \gbc{tile} accepts one suffix parameter, the name of the tile, % followed by three numeric expressions and a boolean. \gbc{unit} should % be a dimension in device units and is the unit of length for all high % level drawing commands within the environment. \gbc{width} and % \gbc{height} specify the size of the tile in multiples of \gbc{unit}, and % \gbc{clipit} is a boolean that determines if the resulting picture is % clipped to the rectangle these parameters determine. For example,\\ % \indent \gbc{tile (fred)(1in, 1, 2, true)} \\ % starts a tile named \gbc{fred} which will be 1 inch wide and 2 inches % tall, and any marks that extend beyond this rectangle are clipped off. % The tile is enclosed in a group to delimit these changes to the basic % drawing parameters. \DescribeRoutine{endtile}\gbc{endtile} merely % implements the clipping and then closes the group. % % In \MF, the picture should be a whole number of pixels in size, so that % the tiles fit perfectly together. The fact that shifts must be integer % values is only mildly relevant, because the placement code does the % rounding. % % For tesselation (filling with tiles) we need to know various properties % of the tile, so a tile is a composite object consisting of a picture, % \gbc{fred.pic} in our example (the actual tile) and a pair % \gbc{fred.dims} of the dimensions (in device units). We used to % save the \gbc{clipit} parameter in \gbc{atile.clipon}, but it was never % used. We also used to have separate numerics \gbc{atile.wd} and % \gbc{atile.ht} but they only got used together as a pair. % \begin{macrocode} def tile (suffix atile) (expr unit, width, height, clipit) = picture atile.pic; atile.pic := nullpicture; pair atile.dims; % atile.dims := round ((width, height)*unit); % atile.dims := (width, height)*unit; begingroup % \end{macrocode} % We do a subset of what we do in \gbc{beginmfpic}, redefining % \gbc{active_plane} so that all drawing commands that add to it will % contribute to the tile, and adapting \gbc{ztr} to the tile dimensions. % Re also redefine \gbc{xneg}, et al., for the benefit of \gbc{levelset}. % \begin{macrocode} save active_plane; def active_plane = atile.pic enddef; save ztr, vtr; transform ztr, vtr; ztr := identity scaled unit; vtr := ztr; save xneg, xpos, yneg, ypos; xneg := 0; xpos := width; yneg := 0; ypos := height; % \end{macrocode} % To implement \gbc{clipit}, we set the current clipping path array % \gbc{ClipPath[\,]} to the boundary of the tile. Note that this turns off % user-defined clipping paths, which are unlikely to be correct for the % local tile coordinates. % \begin{macrocode} save ClipOn; boolean ClipOn; if clipit: ClipOn := true; setarray (path) (ClipPath) (rect(origin, atile.dims)); 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 the needed % components are defined and of the correct type. % \begin{macrocode} vardef is_tile (suffix atile) = (known atile.pic ) and (picture atile.pic) and (known atile.dims) and (pair atile.dims ) 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{getbbox} which takes two pair variable and a path % expression, and sets the pairs to the lower left corner and upper right % corner, respectively, of a rectangle enclosing the path. The bounding % box macros are used on paths in device coordinates, but there is no % intrinsic reason that has to be so: they will return the bounding box in % whatever coordinates the supplied path is in. % % \DescribeRoutine{getbbox} % One can get a rather loose bounding rectangle by using the fact that % each segment of a path (from \mfc{point j of g} to \mfc{point j+1 of g}) % is contained in the convex set determined by all 4 control points for % that segment. So we get a containing rectangle by getting the smallest % and largest values of the $x$- and $y$-coordinates of all those points. % We can get a considerably tighter fit if we cut each segment in half % (or more) before doing that. A calling routine is expected to save and % declare the suffixes \gbc{ll} and \gbc{ur}. Within \grafbase{} commands, % \gbc{getbbox} is always called by \gbc{setbbox}, which does this. % % \DescribeRoutine{ctrlsbbox} % There is a difference between ``\mfc{postcontrol 0 of (subpath (j,j+1/2) % of p)}'' and ``\mfc{postcontrol j of p}''. To gain the tighter box we have % to look at the former. \gbc{ctrlsbbox} just updates the previously found % corners \gbc{ll} and \gbc{ur} of the bounding box based on the controls % of the path segment \gbc{p}, and the calling routine \gbc{getbbox} passes % it half a segment at a time. We don't examine the endpoints of % the half-segment: one has already been examined by \gbc{getbbox} and % the other (a subdivision point of an original segment) lies on the line % segment connecting two control points, and so can't increase the bbox. % % We've given this potentially unlimited accuracy by allowing the number % of subdivisions (\gbc{bbox_split}) to be arbitrary. We choose 2 for the % default. The \gbc{setsplit} command (subsection~\ref{utilities}) ensures % that \gbc{_s} is integral and positive, just in case \gbc{bbox_split} % somehow isn't. % % This description applies only to \MF, because \MP{} has built-in % facilities for determining the bounding box. % % \RoutineIndex{pnt} % \RoutineIndex{pre} % \RoutineIndex{post} % I got tired of typing long expressions like ``\gbc{(precontrol length % p of p)}'', and now use the following abbreviations. % \begin{macrocode} vardef pnt@# (expr p) = point @# of p enddef; vardef pre@# (expr p) = precontrol @# of p enddef; vardef post@# (expr p) = postcontrol @# of p enddef; def getbbox (suffix ll, ur) expr g = % ll := llcorner g; ur := urcorner g; %<*MF> setsplit (_s) bbox_split; ur := ll := pnt 0 (g); for _j = 1 upto length g: ll := pairmin (ll, pnt[_j] (g)); ur := pairmax (ur, pnt[_j] (g)); endfor for _j = 1 upto _s*(length g): ctrlsbbox (subpath ((_j-1)/_s, _j/_s) of g) (ll, ur); endfor % if showbbox: noclip ( safedraw rect (ll, ur) ); fi enddef; %<*MF> numeric bbox_split; bbox_split := 2; def ctrlsbbox (expr p) (suffix ll, ur) = ll := pairmin ( pairmin (ll, post0 (p)), pre 1 (p) ); ur := pairmax ( pairmax (ur, post0 (p)), pre 1 (p) ); enddef; % % \end{macrocode} % % We also have \gbc{tightbbox} and \gbc{tbbox} in \MF{} but these are no % longer used so we'll omit them from \grafbase, but keep them in the % 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} (accurate enough, assuming pixel units). This is only called by % \gbc{tbbox}, which is never used. % % \DescribeRoutine{xlimit} % \gbc{xlimit(x)} returns a value of true if the path \gbc{g} doesn't % cross the vertical line at \gbc{x}. % \DescribeRoutine{ylimit}\gbc{ylimit(y)} is the same for the horizontal % line at \gbc{y}. % \begin{macrocode} %<*unused> def tightbbox (expr g) (suffix ll, ur) = begingroup interim tolerance := .5; ll := ( (solve _xlimit (-infinity, xpart pnt 0 (g))), (solve _ylimit (-infinity, ypart pnt 0 (g))) ); ur := ( (solve _xlimit ( infinity, xpart pnt 0 (g))), (solve _ylimit ( infinity, ypart pnt 0 (g))) ); endgroup if showbbox: noclip ( safedraw rect (ll, ur) ); fi enddef; vardef _xlimit (expr x) = ((x, -infinity)--(x, infinity)) misses g enddef; vardef _ylimit (expr y) = ((-infinity, y)--(infinity, y)) misses g enddef; % \end{macrocode} % % \DescribeRoutine{tbbox} % \gbc{tbbox} simply calls \gbc{tightbbox} on each of an array of paths % and takes the maximum of all the upper right corners and the minimum of % all the lower left. Same syntax as \gbc{tightbbox} except that, instead % of a path parameter, \gbc{g} must be the name of an array of paths. % This macro is never used elsewhere in \grafbase. % \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; % % \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{coloraddon}. In \MP{} it is basically the primitives \mfc{addto} % and \mfc{withcolor} applied to \gbc{active_plane}, but in \MF{} it adds % when the color is less than 1 (gray or black), otherwise it subtracts % (white). % % \RoutineIndex{colorsafedraw} % The command \gbc{safedraw} merely calls \gbc{colorsafedraw}, which then % calls \gbc{picpath}, which calls \gbc{shpath}. One reason for this % roundabout sequence is to support older files (where \gbc{colorsafedraw} % was not defined). Another is that color handling in \MF{} requires a % picture with pixels of weight 1 or 0 only (\gbc{picpath}). Moreover, % \gbc{shpath} guarantees that the mode's aspect ratio is respected. % \begin{macrocode} def safedraw = colorsafedraw (drawcolor) enddef; def colorsafedraw (expr clr) expr d = begingroup setpicture (v) picpath d; DoClip (v); coloraddon (clr, v); endgroup enddef; % \end{macrocode} % % \subsection{Filling}\label{basicfilling} % % \DescribeRoutine{NoCycle} % This is a common warning for all those commands that require a cycle % (closed path) but an open path is supplied. In addition to the warning % in those commands, we also call \gbc{safedraw} for debugging purposes. % % \DescribeRoutine{safefill}\RoutineIndex{colorsafefill} % The basic \gbc{safefill} simply calls the colored version with the % default parameter \gbc{fillcolor}. \gbc{colorsafefill} takes a color as % its first parameter and a path expression as second. These commands fill % the path in the \gbc{active_plane}. In \MF, when the color is strictly % between $0$ and 1, a gray fill is simulated with the \gbc{shaded} macro. % % To simulate the effect of painting over in gray, the \MF{} version % clears the region before adding the shaded fill. % % \DescribeRoutine{safeunfill} % \gbc{safeunfill} is just \gbc{safefill} with the color \mfc{background}. % In \MF{}, when \gbc{background = white = 1}, this is detected by % \gbc{coloraddon} which then subtracts the picture. We do this inside % \gbc{noclip}, just because it seems a user would expect clipping only % when things are \emph{added}. In \MP{} the white is indeed added, but % conceptually, material is cleared away. % \begin{macrocode} def NoCycle (expr s) expr p = GBwarn s & " cannot be applied to an open path." & " The path will be drawn instead."; safedraw p; enddef; %vardef isgray (expr X) = (X > black) and (X < white) enddef; % def safefill = colorsafefill (fillcolor) enddef; vardef colorsafefill (expr clr) expr c = if cycle c: setpicture (v) interior c; DoClip (v); %<*MF> if isgray (clr): _subto (active_plane) (v); v := nullpicture; v := shaded (clr) c; fi % coloraddon (clr, v); else: NoCycle("fill") c; fi enddef; def safeunfill expr c = if cycle c: noclip (colorsafefill (background) c); else: NoCycle("unfill") 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: NoCycle("clip") c; fi enddef; % \end{macrocode} % % \section{Graph Coordinate Rendering}\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 ("improper expression type.") "The 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}\RoutineIndex{colordrawn} % The command \gbc{drawn} merely calls \gbc{colordrawn} with the default % color \gbc{drawcolor}. Then \gbc{colordrawn} takes a color \gbc{clr} % and a path expression \gbc{f} and returns the same path. In between, % \gbc{zconv (f)} is subjected to \gbc{colorsafedraw}. % \begin{macrocode} def drawn = colordrawn (drawcolor) enddef; vardef colordrawn (expr clr) expr f = colorsafedraw (clr) (zconv (f)); f enddef; % \end{macrocode} % % \DescribeRoutine{colorwiggle} % This is a multi-tasking command that can draw either zigzag or % sinewave shapes depending on the boolean first parameter. For \mfc{true} % we get smooth wiggles, for \mfc{false} we get jagged ones. In the % smooth case, a tension parameter allows an adjustment to the smoothness. % The command \DescribeRoutine{zigzag}\RoutineIndex{colorzigzag} % \gbc{zigzag} calls it with the value \mfc{false} and an arbitrary % value of the tension; % \DescribeRoutine{sinewave}\RoutineIndex{colorsinewave}\gbc{sinewave} % calls it with \mfc{true}, allowing it to pick up the tension parameter. % All expect a quadruple of dimensions to follow % % The reason for using a loop (at the end) that draws the \gbc{sinewave} % path in pieces, is that all the turning can quickly exceed \MF{}'s limit % on the autorounding stack. I'd never heard of this stack until I ran % this without a loop and received the ``capacity exceeded'' message. This % turns out to be a problem mostly when the ratio of \gbc{len} to % \gbc{wid} is too small and the `humps' of the sine are more like % `bulbs'. However it is always a problem with \gbc{corkscrew} (below). % \begin{macrocode} def zigzag = colorzigzag (drawcolor) enddef; def colorzigzag (expr clr) = colorwiggle (false, clr, 0) enddef; def sinewave = colorsinewave (drawcolor) enddef; def colorsinewave = colorwiggle (true) enddef; vardef colorwiggle (expr smth, clr, tens, blen, elen, len, wid) expr f = convertpath (g) f; setuplengtharray (cumlen, totlen, ct) g; save B; if cycle f: B := 0; else: B := abs(blen)/_rescale_factor; totlen := totlen - B - abs(elen)/_rescale_factor; fi setnumeric (n) 2*round (totlen/len*_rescale_factor); if n < 2: colorsafedraw (clr) g; else: save T, U, X, Y, Z, p; pair U, X, Y, Z; path p; T := if cycle f: 0 else: gettime (cumlen, ct) (B) fi; Z := pnt[T] (g); p :=if not cycle f: (subpath (0,T) of g) if smth: {curl 0} ..tension tens.. else: -- fi fi for i = 1 upto n: hide( T := gettime (cumlen, ct) (B+(i/n)*totlen); X := Z; Z := pnt[T] (g); Y := .5[X,Z]; U := sgn (Z-X); ) (Y + (U zscaled (0, if even i: - fi wid))) if smth: {U}..tension tens.. else: -- fi endfor if cycle f: cycle else: if smth: {curl 0} fi (subpath (T, length g) of g) fi; newpicture (v); % shpat