% $Id: mfplain.mp,v 1.9 2005/04/28 06:45:21 taco Exp $ % Public domain. % This is the plain MetaPost base that looks like what's described % in The METAFONTbook. string base_name, base_version; base_name="mfplain"; base_version="0.99"; message "Preloading the plain base, version "&base_version&": preliminaries,"; 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; newinternal smoothing, autorounding, turningcheck, granularity; warningcheck:=1; tracinglostchars:=1; smoothing:=1; autorounding:=2; % These are ignored by MetaPost turningcheck:=1; granularity:=1; def gobble primary g = enddef; def killtext text t = 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; 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; message " 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; black = (0,0,0); white = (1,1,1); red = (1,0,0); green = (0,1,0); blue = (0,0,1); % picture constants picture blankpicture,unitpixel; blankpicture=nullpicture; unitpixel=nullpicture; addto unitpixel contour unitsquare; % string constants string ditto; ditto = char 34; % ASCII double-quote mark % pen constants def capsule_def(suffix s) primary u = def s = u enddef enddef; 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 = u enddef; vardef hround primary x = x enddef; vardef vround primary y = y 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; % 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_; % 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 uuu_: 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:=.1; message " macros for converting units,"; newinternal bpppix_, bp_per_pixel; % drawing is done in `pixel' units bpppix_:=0.02; mm*bpppix_=2.83464; pt*bpppix_=0.99626; dd*bpppix_=1.06601; bp*bpppix_=1; cm*bpppix_=28.34645; pc*bpppix_=11.95517; cc*bpppix_=12.79213; in*bpppix_=72; mm#=2.84528; pt#=1; dd#=1.07001; bp#=1.00375; cm#=28.45276; pc#=12; cc#=12.84010; in#=72.27; newinternal hppp, vppp; hppp:=pt; vppp:=pt; newinternal blacker, o_correction; % device-oriented corrections def define_pixels(text t) = forsuffixes $=t: $:=$.#*pt; endfor enddef; def define_blacker_pixels(text t) = forsuffixes $=t: $:=$.#*pt+blacker; endfor enddef; def define_corrected_pixels(text t) = forsuffixes $=t: $:=$.#*pt*o_correction; endfor enddef; def define_whole_pixels = define_pixels enddef; def define_whole_vertical_pixels = define_pixels enddef; def define_good_x_pixels = define_pixels enddef; def define_good_y_pixels = define_pixels enddef; def define_whole_blacker_pixels = define_blacker_pixels enddef; def define_whole_vertical_blacker_pixels = define_blacker_pixels enddef; def define_horizontal_corrected_pixels = define_corrected_pixels enddef; def lowres_fix(text t) expr ratio = enddef; message " macros and tables for various modes of operation,"; transform currenttransform; def t_ = transformed currenttransform enddef; let o_=\; let _o_=\; def mode_setup = if unknown mode: mode=proof; fi numeric aspect_ratio; transform currenttransform; scantokens if string mode:("input "&mode) else: mode_name[mode] fi; if unknown mag: mag=1; fi if unknown aspect_ratio: aspect_ratio=1; fi bp_per_pixel:=bpppix_*mag; scantokens extra_setup; % the user's special last-minute adjustments if unknown currenttransform: currenttransform=identity; fi clearit; pickup pencircle scaled (.4pt+blacker); enddef; def smode = string mode; mode enddef; string extra_setup, mode_name[]; extra_setup=""; % usually there's nothing special to do vardef magstep primary m = mexp(46.67432m) enddef; def mode_def suffix $ = $:=incr number_of_modes; mode_name[$]:=str$ & "_"; expandafter quote def scantokens mode_name[$] enddef; newinternal number_of_modes; newinternal proofing; % <0 to supress output; >1 to do labels color proofcolor; % color for output when proofing>0 proofcolor =.3[white,black]; color background; background = white; % proof mode: for initial design of characters mode_def proof = proofing:=2; % yes, we're making full proofs fontmaking:=0; % no, we're not making a font tracingtitles:=1; % yes, show titles online blacker:=0; % no additional blackness o_correction:=1; % no reduction in overshoot if unknown mag: mag=36; else: mag:=36mag; fi enddef; % smoke mode: for label-free proofs to mount on the wall mode_def smoke = proof_; % same as proof mode, except: proofing:=1; % yes, we're making unlabeled proofs proofcolor:=black; % with solid black pixels let makebox=maketicks; % make the boxes less obtrusive if unknown mag: mag=36; else: mag:=36mag; fi enddef; % lowres mode: for certain devices that print 200 pixels per inch mode_def lowres = proofing:=0; % no, we're not making proofs fontmaking:=1; % yes, we are making a font tracingtitles:=0; % no, don't show titles at all blacker:=0; % no extra blackness with PostScript o_correction:=1; % no reduction in overshoot enddef; localfont:=lowres; % the mode most commonly used to make fonts % It is not likely that additional modes are needed, but if they are, % additional mode_def commands should be in another input file that % gets loaded after the PLAIN base. The auxiliary file should set % base_version:=base_version&"/localname". message " macros for drawing and filling,"; def pc_ = hide(if proofing>0: def pc_=do_pc_ enddef; else: def pc_= enddef; fi) pc_ enddef; def do_pc_ = withcolor proofcolor enddef; linejoin:=rounded; % parameters that effect drawing linecap:=rounded; miterlimit:=10; pen currentpen; picture currentpicture; def fill expr c = addto currentpicture contour c t_ pc_ enddef; def draw expr p = addto currentpicture if picture p: also p else: doublepath p t_ withpen currentpen fi pc_ enddef; def filldraw expr c = addto currentpicture contour c t_ withpen currentpen pc_ enddef; def drawdot expr z = addto currentpicture contour makepath currentpen shifted z t_ pc_ 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; 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 good.x primary x = x enddef; vardef good.y primary y = y enddef; vardef good.lft primary z = z enddef; vardef good.rt primary z = z enddef; vardef good.top primary z = z enddef; vardef good.bot primary z = z 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; message " macros for proof labels and rules,"; string defaultfont; newinternal defaultscale, labeloffset; defaultfont = "cmr10"; defaultscale := 1; labeloffset := 3; vardef makelabel@#(expr s,z) = % puts string s near point z save p; picture p; if known z: p = s infont defaultfont scaled (defaultscale/bp_per_pixel); draw p shifted (z t_ + labeloffset/bp_per_pixel*laboff@# - (labxf@#*lrcorner p + labyf@#*ulcorner p + (1-labxf@#-labyf@#)*llcorner p ) ) withcolor black; interim linecap:=rounded; draw z withpen pencircle scaled (3/bp_per_pixel) withcolor black; fi enddef; string lcode_; % just in case someone refers to this pair laboff, laboff.lft, laboff.rt, laboff.top, laboff.bot; 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=laboff.top; labxf=labxf.top; labyf=labyf.top; % There could be fancy code to keep labels from overlapping! vardef labels@#(text t) = if proofing>1: forsuffixes $=t: makelabel@#(str$,z$); endfor fi enddef; vardef penlabels@#(text t) = if proofing>1: forsuffixes $$=l,,r: forsuffixes $=t: makelabel@#(str$.$$,z$.$$); endfor endfor fi 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; def proofrule(expr w,z) = begingroup interim linecap:=squared; draw w..z withpen pencircle scaled (.4/bp_per_pixel) withcolor black; endgroup enddef; def screenrule(expr w,z) = enddef; pen rulepen; def makegrid(text xlist,ylist) = xmin_ := min(xlist); xmax_ := max(xlist); ymin_ := min(ylist); ymax_ := max(ylist); for x=xlist: proofrule((x,ymin_), (x,ymax_)); endfor for y=ylist: proofrule((xmin_,y), (xmax_,y)); endfor enddef; vardef titlefont suffix $ = enddef; vardef labelfont suffix $ = defaultfont:=str$ enddef; vardef grayfont suffix $ = enddef; vardef slantfont suffix $ = enddef; def proofoffset primary z = enddef; vardef proofrulethickness expr x = rulepen := pencircle scaled x enddef; message " macros for character and font administration,"; def beginchar(expr c,w_sharp,h_sharp,d_sharp) = begingroup charcode:=if known c: byte c else: 0 fi; charwd:=w_sharp; charht:=h_sharp; chardp:=d_sharp; w:=charwd*pt; h:=charht*pt; d:=chardp*pt; charic:=0; clearxy; clearit; clearpen; scantokens extra_beginchar; enddef; def italcorr expr x_sharp = if x_sharp>0: charic:=x_sharp fi enddef; def change_width = enddef; def endchar = scantokens extra_endchar; if proofing>0: makebox(proofrule); fi chardx:=w; % desired width of the character in pixels shipit; endgroup enddef; string extra_beginchar, extra_endchar; extra_beginchar=extra_endchar=""; def makebox(text r) = for y=0,h,-d: r((0,y),(w,y)); endfor % horizontals for x=0,w: r((x,-d),(x,h)); endfor % verticals if charic<>0: r((w+charic*pt,h),(w+charic*pt,.5h)); fi enddef; def maketicks(text r) = for y=0,h,-d: r((0,y),(10,y)); r((w-10,y),(w,y)); endfor for x=0,w: r((x,10-d),(x,-d)); r((x,h-10),(x,h)); endfor if charic<>0: r((w+charic*pt,h-10),(w+charic*pt,h)); fi enddef; def font_size expr x = designsize:=x enddef; def font_slant expr x = fontdimen 1: x enddef; def font_normal_space expr x = fontdimen 2: x enddef; def font_normal_stretch expr x = fontdimen 3: x enddef; def font_normal_shrink expr x = fontdimen 4: x enddef; def font_x_height expr x = fontdimen 5: x enddef; def font_quad expr x = fontdimen 6: x enddef; def font_extra_space expr x = fontdimen 7: x enddef; def font_identifier expr x = font_identifier_:=x enddef; def font_coding_scheme expr x = font_coding_scheme_:=x enddef; string font_identifier_, font_coding_scheme_; font_identifier_=font_coding_scheme_="UNSPECIFIED"; message "and a few last-minute items."; vardef z@#=(x@#,y@#) enddef; def openit = enddef; def showit = enddef; def clearxy = save x,y enddef; def clearit = currentpicture:=nullpicture enddef; def shipit = if proofing>=0: shipout currentpicture transformed (identity shifted (xoffset,yoffset) scaled bp_per_pixel) fi enddef; def cull text t = enddef; def cullit = enddef; newinternal xoffset, yoffset; def screenchars = enddef; def screenstrokes = enddef; def imagerules = enddef; def gfcorners = enddef; def nodisplays = enddef; def notransforms = let t_ = \ enddef; let bye = end; outer end,bye; clear_pen_memory; % initialize the `savepen' mechanism mode_setup; % establish proof mode as the default numeric mode,mag; % but leave mode and mag undefined dump;