% This file gives the macros for plain MetaPost
% It contains all the features of plain METAFONT except those specific to
% font-making. (See The METAFONTbook by D.E. Knuth).
% There are also a number macros labeling figures etc.
string base_name, base_version; base_name="plain"; base_version="0.62";
message "Preloading the plain mem file, version "&base_version;
delimiters (); % this makes parentheses behave like parentheses
def upto = step 1 until enddef; % syntactic sugar
def downto = step -1 until enddef;
def exitunless expr c = exitif not c enddef;
let relax = \; % ignore the word `relax', as in TeX
let \\ = \; % double relaxation is like single
def ]] = ] ] enddef; % right brackets should be loners
def -- = {curl 1}..{curl 1} enddef;
def --- = .. tension infinity .. enddef;
def ... = .. tension atleast 1 .. enddef;
def gobble primary g = enddef;
primarydef g gobbled gg = enddef;
def hide(text t) = exitif numeric begingroup t;endgroup; enddef;
def ??? = hide(interim showstopping:=1; showdependencies) enddef;
def stop expr s = message s; gobble readstring enddef;
warningcheck:=1;
tracinglostchars:=1;
def interact = % sets up to make "show" commands stop
hide(showstopping:=1; tracingonline:=1) enddef;
def loggingall = % puts tracing info into the log
tracingcommands:=3; tracingtitles:=1; tracingequations:=1;
tracingcapsules:=1; tracingspecs:=2; tracingchoices:=1; tracinglostchars:=1;
tracingstats:=1; tracingoutput:=1; tracingmacros:=1; tracingrestores:=1;
enddef;
def tracingall = % turns on every form of tracing
tracingonline:=1; showstopping:=1; loggingall enddef;
def tracingnone = % turns off every form of tracing
tracingcommands:=0; tracingtitles:=0; tracingequations:=0;
tracingcapsules:=0; tracingspecs:=0; tracingchoices:=0; tracinglostchars:=0;
tracingstats:=0; tracingoutput:=0; tracingmacros:=0; tracingrestores:=0;
enddef;
%% dash patterns
vardef dashpattern(text t) =
save on, off, w;
let on=_on_;
let off=_off_;
w = 0;
nullpicture t
enddef;
tertiarydef p _on_ d =
begingroup save pic;
picture pic; pic=p;
addto pic doublepath (w,w)..(w+d,w);
w := w+d;
pic shifted (0,d)
endgroup
enddef;
tertiarydef p _off_ d =
begingroup w:=w+d;
p shifted (0,d)
endgroup
enddef;
%% basic constants and mathematical macros
% numeric constants
newinternal eps,epsilon,infinity,_;
eps := .00049; % this is a pretty small positive number
epsilon := 1/256/256; % but this is the smallest
infinity := 4095.99998; % and this is the largest
_ := -1; % internal constant to make macros unreadable but shorter
newinternal mitered, rounded, beveled, butt, squared;
mitered:=0; rounded:=1; beveled:=2; % linejoin types
butt:=0; rounded:=1; squared:=2; % linecap types
% pair constants
pair right,left,up,down,origin;
origin=(0,0); up=-down=(0,1); right=-left=(1,0);
% path constants
path quartercircle,halfcircle,fullcircle,unitsquare;
fullcircle = makepath pencircle;
halfcircle = subpath (0,4) of fullcircle;
quartercircle = subpath (0,2) of fullcircle;
unitsquare=(0,0)--(1,0)--(1,1)--(0,1)--cycle;
% transform constants
transform identity;
for z=origin,right,up: z transformed identity = z; endfor
% color constants
color black, white, red, green, blue, background;
black = (0,0,0);
white = (1,1,1);
red = (1,0,0);
green = (0,1,0);
blue = (0,0,1);
background = white; % The user can reset this
% picture constants
picture blankpicture,evenly,withdots;
blankpicture=nullpicture; % `display blankpicture...'
evenly=dashpattern(on 3 off 3); % `dashed evenly'
withdots=dashpattern(off 2.5 on 0 off 2.5); % `dashed withdots'
% string constants
string ditto, EOF;
ditto = char 34; % ASCII double-quote mark
EOF = char 0; % end-of-file for readfrom and write..to
% pen constants
pen pensquare,penrazor,penspeck;
pensquare = makepen(unitsquare shifted -(.5,.5));
penrazor = makepen((-.5,0)--(.5,0)--cycle);
penspeck=pensquare scaled eps;
% nullary operators
vardef whatever = save ?; ? enddef;
% unary operators
let abs = length;
vardef round primary u =
if numeric u: floor(u+.5)
elseif pair u: (round xpart u, round ypart u)
else: u fi enddef;
vardef ceiling primary x = -floor(-x) enddef;
vardef byte primary s =
if string s: ASCII fi s enddef;
vardef dir primary d = right rotated d enddef;
vardef unitvector primary z = z/abs z enddef;
vardef inverse primary T =
transform T_; T_ transformed T = identity; T_ enddef;
vardef counterclockwise primary c =
if turningnumber c <= 0: reverse fi c enddef;
vardef tensepath expr r =
for k=0 upto length r - 1: point k of r --- endfor
if cycle r: cycle else: point infinity of r fi enddef;
vardef center primary p = .5[llcorner p, urcorner p] enddef;
% binary operators
primarydef x mod y = (x-y*floor(x/y)) enddef;
primarydef x div y = floor(x/y) enddef;
primarydef w dotprod z = (xpart w * xpart z + ypart w * ypart z) enddef;
primarydef x**y = if y=2: x*x else: takepower y of x fi enddef;
def takepower expr y of x =
if x>0: mexp(y*mlog x)
elseif (x=0) and (y>0): 0
else: 1
if y=floor y:
if y>=0: for n=1 upto y: *x endfor
else: for n=_ downto y: /x endfor
fi
else: hide(errmessage "Undefined power: " & decimal x&"**"&decimal y)
fi fi enddef;
vardef direction expr t of p =
postcontrol t of p - precontrol t of p enddef;
vardef directionpoint expr z of p =
a_:=directiontime z of p;
if a_<0: errmessage("The direction doesn't occur"); fi
point a_ of p enddef;
secondarydef p intersectionpoint q =
begingroup save x_,y_; (x_,y_)=p intersectiontimes q;
if x_<0: errmessage("The paths don't intersect"); origin
else: .5[point x_ of p, point y_ of q] fi endgroup
enddef;
tertiarydef p softjoin q =
begingroup c_:=fullcircle scaled 2join_radius shifted point 0 of q;
a_:=ypart(c_ intersectiontimes p); b_:=ypart(c_ intersectiontimes q);
if a_<0:point 0 of p{direction 0 of p} else: subpath(0,a_) of p fi
... if b_<0:{direction infinity of q}point infinity of q
else: subpath(b_,infinity) of q fi endgroup enddef;
newinternal join_radius,a_,b_; path c_;
path cuttings; % what got cut off
tertiarydef a cutbefore b = % tries to cut as little as possible
begingroup save t;
(t, whatever) = a intersectiontimes b;
if t<0:
cuttings:=point 0 of a;
a
else: cuttings:= subpath (0,t) of a;
subpath (t,length a) of a
fi
endgroup
enddef;
tertiarydef a cutafter b =
reverse (reverse a cutbefore b)
hide(cuttings:=reverse cuttings)
enddef;
% special operators
vardef incr suffix $ = $:=$+1; $ enddef;
vardef decr suffix $ = $:=$-1; $ enddef;
def reflectedabout(expr w,z) = % reflects about the line w..z
transformed
begingroup transform T_;
w transformed T_ = w; z transformed T_ = z;
xxpart T_ = -yypart T_; xypart T_ = yxpart T_; % T_ is a reflection
T_ endgroup enddef;
def rotatedaround(expr z, d) = % rotates d degrees around z
shifted -z rotated d shifted z enddef;
let rotatedabout = rotatedaround; % for roundabout people
vardef min(expr u)(text t) = % t is a list of numerics, pairs, or strings
save u_; setu_ u; for uu = t: if uu<u_: u_:=uu; fi endfor
u_ enddef;
vardef max(expr u)(text t) = % t is a list of numerics, pairs, or strings
save u_; setu_ u; for uu = t: if uu>u_: u_:=uu; fi endfor
u_ enddef;
def setu_ primary u =
if pair u: pair u_ elseif string u: string u_ fi;
u_=u enddef;
def flex(text t) = % t is a list of pairs
hide(n_:=0; for z=t: z_[incr n_]:=z; endfor
dz_:=z_[n_]-z_1)
z_1 for k=2 upto n_-1: ...z_[k]{dz_} endfor ...z_[n_] enddef;
newinternal n_; pair z_[],dz_;
def superellipse(expr r,t,l,b,s)=
r{up}...(s[xpart t,xpart r],s[ypart r,ypart t]){t-r}...
t{left}...(s[xpart t,xpart l],s[ypart l,ypart t]){l-t}...
l{down}...(s[xpart b,xpart l],s[ypart l,ypart b]){b-l}...
b{right}...(s[xpart b,xpart r],s[ypart r,ypart b]){r-b}...cycle enddef;
vardef interpath(expr a,p,q) =
for t=0 upto length p-1: a[point t of p, point t of q]
..controls a[postcontrol t of p, postcontrol t of q]
and a[precontrol t+1 of p, precontrol t+1 of q] .. endfor
if cycle p: cycle
else: a[point infinity of p, point infinity of q] fi enddef;
vardef solve@#(expr true_x,false_x)= % @#(true_x)=true, @#(false_x)=false
tx_:=true_x; fx_:=false_x;
forever: x_:=.5[tx_,fx_]; exitif abs(tx_-fx_)<=tolerance;
if @#(x_): tx_ else: fx_ fi :=x_; endfor
x_ enddef; % now x_ is near where @# changes from true to false
newinternal tolerance, tx_,fx_,x_; tolerance:=.01;
vardef buildcycle(text ll) =
save ta_, tb_, k_, i_, pp_; path pp_[];
k_=0;
for q=ll: pp_[incr k_]=q; endfor
i_=k_;
for i=1 upto k_:
(ta_[i], length pp_[i_]-tb_[i_]) =
pp_[i] intersectiontimes reverse pp_[i_];
if ta_[i]<0:
errmessage("Paths "& decimal i &" and "& decimal i_ &" don't intersect");
fi
i_ := i;
endfor
for i=1 upto k_: subpath (ta_[i],tb_[i]) of pp_[i] .. endfor
cycle
enddef;
%% units of measure
mm=2.83464; pt=0.99626; dd=1.06601; bp:=1;
cm=28.34645; pc=11.95517; cc=12.79213; in:=72;
vardef magstep primary m = mexp(46.67432m) enddef;
%% macros for drawing and filling
def drawoptions(text t) =
def _op_ = t enddef
enddef;
linejoin:=rounded; % parameters that effect drawing
linecap:=rounded;
miterlimit:=10;
drawoptions();
pen currentpen;
picture currentpicture;
def fill expr c = addto currentpicture contour c _op_ enddef;
def draw expr p =
addto currentpicture
if picture p:
also p
else:
doublepath p withpen currentpen
fi
_op_
enddef;
def filldraw expr c =
addto currentpicture contour c withpen currentpen
_op_ enddef;
def drawdot expr z =
addto currentpicture contour makepath currentpen shifted z
_op_ enddef;
def unfill expr c = fill c withcolor background enddef;
def undraw expr p = draw p withcolor background enddef;
def unfilldraw expr c = filldraw c withcolor background enddef;
def undrawdot expr z = drawdot z withcolor background enddef;
def erase text t =
def _e_ = withcolor background hide(def _e_=enddef;) enddef;
t _e_
enddef;
def _e_= enddef;
def cutdraw text t =
begingroup interim linecap:=butt; draw t _e_; endgroup enddef;
vardef image(text t) =
save currentpicture;
picture currentpicture;
currentpicture := nullpicture;
t;
currentpicture
enddef;
def pickup secondary q =
if numeric q: numeric_pickup_ else: pen_pickup_ fi q enddef;
def numeric_pickup_ primary q =
if unknown pen_[q]: errmessage "Unknown pen"; clearpen
else: currentpen:=pen_[q];
pen_lft:=pen_lft_[q];
pen_rt:=pen_rt_[q];
pen_top:=pen_top_[q];
pen_bot:=pen_bot_[q];
currentpen_path:=pen_path_[q] fi; enddef;
def pen_pickup_ primary q =
currentpen:=q;
pen_lft:=xpart penoffset down of currentpen;
pen_rt:=xpart penoffset up of currentpen;
pen_top:=ypart penoffset left of currentpen;
pen_bot:=ypart penoffset right of currentpen;
path currentpen_path; enddef;
newinternal pen_lft,pen_rt,pen_top,pen_bot,pen_count_;
vardef savepen = pen_[incr pen_count_]=currentpen;
pen_lft_[pen_count_]=pen_lft;
pen_rt_[pen_count_]=pen_rt;
pen_top_[pen_count_]=pen_top;
pen_bot_[pen_count_]=pen_bot;
pen_path_[pen_count_]=currentpen_path;
pen_count_ enddef;
def clearpen = currentpen:=nullpen;
pen_lft:=pen_rt:=pen_top:=pen_bot:=0;
path currentpen_path;
enddef;
def clear_pen_memory =
pen_count_:=0;
numeric pen_lft_[],pen_rt_[],pen_top_[],pen_bot_[];
pen currentpen,pen_[];
path currentpen_path, pen_path_[];
enddef;
vardef lft primary x = x + if pair x: (pen_lft,0) else: pen_lft fi enddef;
vardef rt primary x = x + if pair x: (pen_rt,0) else: pen_rt fi enddef;
vardef top primary y = y + if pair y: (0,pen_top) else: pen_top fi enddef;
vardef bot primary y = y + if pair y: (0,pen_bot) else: pen_bot fi enddef;
vardef penpos@#(expr b,d) =
(x@#r-x@#l,y@#r-y@#l)=(b,0) rotated d;
x@#=.5(x@#l+x@#r); y@#=.5(y@#l+y@#r) enddef;
def penstroke text t =
forsuffixes e = l,r: path_.e:=t; endfor
fill path_.l -- reverse path_.r -- cycle enddef;
path path_.l,path_.r;
%% High level drawing commands
newinternal ahlength, ahangle;
ahlength := 4; % default arrowhead length 4bp
ahangle := 45; % default head angle 45 degrees
vardef arrowhead expr p =
save q,e; path q; pair e;
e = point length p of p;
q = gobble(p shifted -e cutafter makepath(pencircle scaled 2ahlength))
cuttings;
(q rotated .5ahangle & reverse q rotated -.5ahangle -- cycle) shifted e
enddef;
path _apth;
def drawarrow expr p = _apth:=p; _finarr enddef;
def drawdblarrow expr p = _apth:=p; _findarr enddef;
def _finarr text t =
draw _apth t;
filldraw arrowhead _apth t
enddef;
def _findarr text t =
draw _apth t;
fill arrowhead _apth withpen currentpen t;
fill arrowhead reverse _apth withpen currentpen t
enddef;
%% macros for labels
newinternal bboxmargin; bboxmargin:=2bp;
vardef bbox primary p =
llcorner p-(bboxmargin,bboxmargin) -- lrcorner p+(bboxmargin,-bboxmargin)
-- urcorner p+(bboxmargin,bboxmargin) -- ulcorner p+(-bboxmargin,bboxmargin)
-- cycle
enddef;
string defaultfont;
newinternal defaultscale, labeloffset;
defaultfont = "cmr10";
defaultscale := 1;
labeloffset := 3bp;
vardef thelabel@#(expr s,z) = % Position s near z
save p; picture p;
if picture s: p=s
else: p = s infont defaultfont scaled defaultscale
fi;
p shifted (z + labeloffset*laboff@# -
(labxf@#*lrcorner p + labyf@#*ulcorner p
+ (1-labxf@#-labyf@#)*llcorner p
)
)
enddef;
def label = draw thelabel enddef;
vardef dotlabel@#(expr s,z) =
label@#(s,z);
interim linecap:=rounded;
draw z withpen pencircle scaled 3bp;
enddef;
def makelabel = dotlabel enddef;
pair laboff, laboff.lft, laboff.rt, laboff.top, laboff.bot;
pair laboff.ulft, laboff.llft, laboff.urt, laboff.lrt;
laboff =(0,0); labxf =.5; labyf =.5;
laboff.lft=(-1,0); labxf.lft=1; labyf.lft=.5;
laboff.rt =(1,0); labxf.rt =0; labyf.rt =.5;
laboff.bot=(0,-1); labxf.bot=.5; labyf.bot=1;
laboff.top=(0,1); labxf.top=.5; labyf.top=0;
laboff.ulft=(-.7,.7);labxf.ulft=1; labyf.ulft=0;
laboff.urt=(.7,.7); labxf.urt=0; labyf.urt=0;
laboff.llft=-(.7,.7);labxf.llft=1; labyf.llft=1;
laboff.lrt=(.7,-.7); labxf.lrt=0; labyf.lrt=1;
vardef labels@#(text t) =
forsuffixes $=t:
label@#(str$,z$); endfor
enddef;
vardef dotlabels@#(text t) =
forsuffixes $=t:
dotlabel@#(str$,z$); endfor
enddef;
vardef penlabels@#(text t) =
forsuffixes $$=l,,r: forsuffixes $=t:
makelabel@#(str$.$$,z$.$$); endfor endfor
enddef;
def range expr x = numtok[x] enddef;
def numtok suffix x=x enddef;
tertiarydef m thru n =
m for x=m+1 step 1 until n: , numtok[x] endfor enddef;
%% Overall adminstration
string extra_beginfig, extra_endfig;
extra_beginfig = extra_endfig = "";
def beginfig(expr c) =
begingroup
charcode:=c;
clearxy; clearit; clearpen;
pickup defaultpen;
drawoptions();
scantokens extra_beginfig;
enddef;
def endfig =
scantokens extra_endfig;
shipit;
endgroup
enddef;
%% last-minute items
vardef z@#=(x@#,y@#) enddef;
def clearxy = save x,y enddef;
def clearit = currentpicture:=nullpicture enddef;
def shipit = shipout currentpicture enddef;
let bye = end; outer end,bye;
clear_pen_memory; % initialize the `savepen' mechanism
clearit;
newinternal defaultpen;
pickup pencircle scaled .5bp; % set default line width
defaultpen := savepen;
|