% Blackboard bold base file, containing lots of horrible hacks. % Alan Jeffrey, 12--18 Dec 1989. % DIGITIZATION % I'm doing all my own digitization, so I don't need help from MF. autorounding := 0; % To get the unsharp version of x#, say unsharp x#. def unsharp = hppp * enddef; % The only pen I'm working with is of size pen_size. This makes life % rather easier. To find the x-coordinate whose left-hand edge is % at hround x, say leftround x, and similarly for rightround, % topround and bottomround. def leftround expr x = hround x + 1/2 pen_size enddef; def rightround expr x = hround x - 1/2 pen_size enddef; def topround expr y = vround y - 1/2 pen_size enddef; def bottomround expr y = vround y + 1/2 pen_size enddef; % Using this, I can give the equivalent of define_whole_pixels... def define_whole_top_pixels (text t) = forsuffixes $ = t: $ := topround unsharp $.#; endfor enddef; def define_whole_bottom_pixels (text t) = forsuffixes $ = t: $ := bottomround unsharp $.#; endfor enddef; % POINTS % The point y~x is short for (x,y). The reason for switching the % points around is that I find it easier to say Top~Left than % (Left, Top). primarydef x ~ y = (y,x) enddef; % To find the average of two points, say p -+- q. primarydef x ~ y = (y,x) enddef; tertiarydef x -+- y = .5[x,y] enddef; % To get the point 1/2fatness above p, say above p. This is useful % for drawing lines fatness in width. Similarly we have commands % below, leftof and rightof. def above secondary p = p + 1/2fatness * up enddef; def below secondary p = p + 1/2fatness * down enddef; def leftof secondary p = p + 1/2fatness * left enddef; def rightof secondary p = p + 1/2fatness * right enddef; % The command farleftof is equivalent to leftof leftof, and similarly % farrightof. def farleftof secondary p = p + fatness * left enddef; def farrightof secondary p = p + fatness * right enddef; % To get curves with o-correction, we need to be able to move % o pixels up or down. def oabove secondary p = p + o * up enddef; def obelow secondary p = p + o * down enddef; % DECLARATIONS % To declare a new variable foo of type T, say var (T) foo. def var (text type) text declarations = save declarations; type declarations enddef; % PATHS % Given a cyclic path p, outline p draws the path and fills the inside % with white. This is stolen from the MF book, exercise 13.11. def outline expr c = begingroup picture region; region := nullpicture; interim turningcheck := 0; addto region contour c; cull region dropping (0,0); cullit; addto currentpicture also -region; cullit; draw c endgroup enddef; % Given a path p, leftside p is the path 1/2 fatness to its left, % and similarly rightside. def leftside primary apath = apath shifted (1/2fatness * left) enddef; def rightside primary apath = apath shifted (1/2fatness * right) enddef; % Given a path p, fatten p draws the leftside of p, % the rightside of p, and joins them up with straight lines. def fatten expr apath = leftside apath -- (reverse rightside apath) -- cycle enddef; % Given a point p, splodge p draws an o-corrected circle of radius % fatness around p. def splodge expr apoint = above apoint + o*up {right} .. rightof apoint + o*right {down} .. below apoint + o*down {left} .. leftof apoint + o*left {up} .. cycle enddef; % The command splodgel does the same, but doesn't close the cycle, % and leaves the current point at the left of the circle. def splodgel tertiary apoint = leftof apoint + o*left {up} .. above apoint + o*up {right} .. rightof apoint + o*right {down} .. below apoint + o*down {left} .. leftof apoint + o*left {up} enddef; % Similarly, splodger leaves the current point at the right of the % circle. def splodger tertiary apoint = rightof apoint + o*right {down} .. below apoint + o*down {left} .. leftof apoint + o*left {up} .. above apoint + o*up {right} .. rightof apoint + o*right {down} enddef; % CLIPPING % sometime (p, q) gives the time along p when it intersects q. def sometime (expr apath, bpath) = xpart (apath intersectiontimes bpath) enddef; % othertime (p, q) gives another time at which p intersects q. def othertime (expr apath, bpath) = length apath - sometime (reverse apath) (reverse bpath) enddef; % firsttime (p, q) gives the smallest of sometime (p,q) and othertime (p,q). def firsttime (expr apath, bpath) = min (sometime (apath) (bpath), othertime (apath) (bpath)) enddef; % lasttime (p, q) gives the largest of the two times. def lasttime (expr apath, bpath) = max (sometime (apath) (bpath), othertime (apath) (bpath)) enddef; % We can then clip p with q by finding the subpath of p from 0 to % the time p intersects q. def cliponce (expr apath, clippath) = subpath (0, sometime (apath) (clippath)) of apath enddef; % Similarly, if p intersects q twice, we can find the path between % the two times it intersects with cliptwice. def cliptwice (expr apath, clippath) = subpath (firsttime (apath) (clippath), lasttime (apath) (clippath)) of apath enddef; % Given a path p and two paths q and r which intersect p, % we can find the path between when p crosses q and when p crosses r. % someclipbetween (p, q, r) will always start at q and finish at r. def someclipbetween (expr apath, firstclip, secondclip) = subpath (sometime (apath) (firstclip), sometime (apath) (secondclip)) of apath enddef; % firstclipbetween does the same, but if p intersects q and r more % than once, it gives the first clipping. def firstclipbetween (expr apath, firstclip, secondclip) = subpath (firsttime (apath) (firstclip), firsttime (apath) (secondclip)) of apath enddef; % lastclipbetween gives the last clipping. def lastclipbetween (expr apath, firstclip, secondclip) = subpath (lasttime (apath) (firstclip), lasttime (apath) (secondclip)) of apath enddef; % We can join these together and clip fat lines. def fatcliponce (expr apath, clippath) = cliponce (leftside apath) (clippath) -- someclipbetween (clippath) (leftside apath) (rightside apath) -- reverse cliponce (rightside apath) (clippath) -- cycle enddef; def fatcliptwice (expr apath, clippath) = cliptwice (leftside apath) (clippath) -- lastclipbetween (clippath) (leftside apath) (rightside apath) -- cliptwice (rightside apath) (reverse clippath) -- firstclipbetween (clippath) (rightside apath) (leftside apath) -- cycle enddef; % BBCHAR. % bbchar (c) (l#, w#, r#) (t#, b#) begins a character at code c, % of width w# with l# gap at the left and r# gap at the right. % It's topmost point is at t# and its bottommost point at b#. % From these parameters we calculate Width (the width of the character % in whole pixels) and hardTop and hardBottom (the exact top and bottom % of the character). Top is then 1/2 pensize from the top of the character, % and Bottom is 1/2 pensize from the bottom. This means if we draw a line % through top, the top of it will exactly touch the top. We then calculate % Left, Middle, and Right in the same way, using calculateLeftetc. def bbchar (expr code) (expr sharphardLeft, sharpWidth, sharprightgap) (expr sharphardTop, sharphardBottom) = beginchar (code) (sharphardLeft + sharpWidth + sharprightgap) (max (sharphardTop, 0pt#)) (max (-sharphardBottom, 0pt#)); save Top, Bottom, Horizon, hardTop, hardBottom, Width, hardLeft, hardRight, Left, Right, Middle; hardTop# = sharphardTop; hardBottom# = sharphardBottom; Width# = sharpWidth; hardLeft# = sharphardLeft; hardRight# = sharphardLeft + sharpWidth; define_whole_pixels (Width); define_whole_vertical_pixels (hardTop, hardBottom); Top = topround hardTop; Bottom = bottomround hardBottom; Horizon = .5 [Top, Bottom]; calculateLeftetc; pickup pencircle scaled pen_size; enddef; def calculateLeftetc = hardLeft := floor (unsharp hardLeft#); hardRight := hardLeft + Width; Left := (hardLeft + 1/2pen_size); Middle := (hardLeft + 1/2Width); Right := (hardRight - 1/2pen_size); enddef; % bbcap is bbchar with the top at ATop# and the bottom at aBottom#. def bbcap (expr code, leftgap, width, rightgap) = bbchar (code) (leftgap, width, rightgap) (ATop#, aBottom#); enddef; % bbnum is bbchar with the dimensions of a number hard-wired. def bbnum (expr code) = bbchar (code) (medgap#, numeral#, medgap#) (oneTop#, aBottom#); enddef; % For characters such as < and > which appear a lot blacker than the % others, we can surround the character by beginblacker n ... endblacker, % which temporarily multiplies fatness by n. def beginblacker expr blackness = begingroup save oldfatness; oldfatness# := fatness#; save fatness; fatness# := oldfatness# * blackness; define_whole_pixels (fatness) enddef; let endblacker = endgroup; % SYMMETRY % To try to get characters symmetric, we need to round the width so there % are the same number of characters on the left of the central vertical % as there are on the right. So if we are symmetrical around a pen of % size 2n, we need to make the width even. If we are symmetrical around % a pen of size 2n+1 we need to make the width odd. This is done with % roundlike (x) y, which rounds y to be even iff x is even. def roundlike (expr x) expr y = 2 * (round (x -+- y)) - x enddef; % To make the character symmetric, we round Width like pen_size. def symmetric = Width := roundlike (pen_size) unsharp Width#; calculateLeftetc enddef; % To make the character symmetric around a fat vertical, we round % Width like fatness + pen_size. def fatsymmetric = Width := roundlike (fatness + pen_size) unsharp Width#; calculateLeftetc enddef; % DRAWING THE CHARACTERS ON THE SCREEN % makebox and maketicks nicked from cmbase, adjusted for this job. def makebox(text rule) = for y=0, hardBottom, hardTop: rule((0,y)t_,(w,y)t_); endfor % horizontals for x=0,hardLeft,hardRight,w: rule((x,hardBottom)t_,(x,hardTop)t_); endfor % verticals enddef; def maketicks(text rule) = for y=0, hardBottom, hardTop: rule((-10,y)t_,(0,y)t_); % horizontals at left rule((w,y)t_,(w+10,y)t_); % horizontals at right endfor for x=0,hardLeft,hardRight,w: rule((x,hardBottom-10)t_,(x,hardBottom)t_); % verticals at bottom rule((x,hardTop)t_,(x,hardTop+10)t_); % verticals at top endfor % verticals at top enddef; % HACKS TO MAKE CMR WORK % Some parameters I never use, but are needed by the cmr parameter files. boolean square_dots, hefty, serifs, monospace, variant_g, low_asterisk, math_fitting; % And that's that.