perm filename PEN0.MF[1,3] blob sn#484029 filedate 1979-10-24 generic text, type C, neo UTF8

COMMENT ⊗ VALID 00004 PAGES C REC PAGE DESCRIPTION C00001 00001 C00002 00002 % This file draws a pen-style font given the following parameters: C00011 00003 if mode=font: ppp=3.6 fntmode tfxmode maxht height*ppp+1 trxy slant C00030 00004 subroutine lserif(index i, var dx, var dy, var m, var theta): C00038 ENDMK C⊗; % This file draws a pen-style font given the following parameters: % % height the height in points of b above the baseline. % % oheight the height in points of o above the baseline. Typically % 3/5 the value of HEIGHT for roman, 5/9 for italic. % % depth the distance in points of the bottom of a p below % the baseline; typically depth = height - oheight. % % capheight The height in points of the tops of capital letters. % Typically equal to 3/2 oheight for italic. % If capheight = 0, it will be set so that the % tops of capital letters are level with the bottoms of % the serifs of the tall lower-case letters; this is % typical for roman fonts. % % owidth The width in points of "o". Typically equal to oheight % for roman, less for italic. % % style should equal either ROMAN or ITALIC; determines % the shape of a and f. (ROMAN and ITALIC must be defined % and be distinct; they might be zero and one, for example.) % % penwidth width in points of pen; typically 1/5 oheight. % % penheight height in points of pen. For a true pen-style font this % should be as small as possible; the pen should draw a % hairline when moved in the right direction. But METAFONT % causes funny things to happen since it digitizes the pen % before drawing. For example, consider a very narrow pen at an % angle of 45 degrees. If narrow enough, the only lattice % points the elipse contains will have x=y; that is the % pen will consists of a single diagonal of points. This % would be OK, except that when such a pen is moved % diagonally in the broad direction METAFONT's rounding % rule causes it to miss half the points it is moving % through. For example, say the pen contains (0,0) and % (1,1) and moves down to the right. Now it will contain % (1,-1) and (2,0). It has missed the point (1,0), which % will never be blackened. This causes streaks in the % letters. % % Thus, if penheight is set to 0, it will be reset to the % smallest value such that this streaking effect does not % occur; this value is a function of penwidth, penangle, % and the resolution of the output device. % % penangle angle of pen; 0 means horizontal, 90 means vertical % Usually 45 for italic and 30 for roman. % % slant the tangent of the angle by which letters are tilted % to the right; typically 0 for roman and .15 for italic, % but you can slant a roman font or leave an italic one % straight. % % serifsize The radius in points of the circular motion used to finish % most strokes on lower-case letters. Typical value: 1/2 PENWIDTH. % % footsize This is the length of the straight, foot-like serifs used % at the bottom of f and on many upper-case letters. % % oslant This is the extra slant given to the round parts of % letters to make them look italic. Typically OSLANT=0 % for roman and OSLANT=.20 for italic. % % nwidth This is the ratio of the width of an "n" to the width % of an "o". (More precisely, it is what the ratio % would be if the characters were drawn with an infinitely fine % pen.) Ann Camp recommends a value of pi/4 ~ .78539 % in her book "Pen Lettering". % % sharp Must be between 0 and 1. The larger it % is the sharper is the turn between the curved and straight % parts of the letters a,b,f,g,h,j,m,n,q,t,u, and y. % Typically .9 for roman, .5 for italic. % % mode If mode=datadisk, the characters will be displayed on % the data-disk as they are drawn. If mode=proof, a file % of proof-sheets will be produced. If mode=font, a font % and tfx file will be produced. For example, the statement % mode + 1 = datadisk=proof=font causes none of these actions % to be taken, so the file is rapidly scanned for errors. % If mode=datadisk and mode≠proof, mode≠font, then TRXX will % be used to shrink the characters in the x-direction to % compensate for the non-square pixels on the screen. % crs must also be defined. % expen Explicit pen. It expen=0, everything is as described % above. Otherwise, the file that uses this one is % expected to have defined three subroutines, % normpen, (which sets the normal pen), steeppen % (which sets the pen to a steep pen for Z), and % flatpen (which sets the pen to a flat pen for Q and the % diagonal stroke of Z). The variables penwidth, penheight, % and penangle must still be defined. % This file defines global variables and useful subroutines. % The programs for drawing the letters themselves are in % the file PEN.MF. As an example of the use of these files, % here is the METAFONT program that creates the font PENI12: % % "Peni12; 12-point italic scrivener's font"; % oheight=5/9 height; % owidth=4/5 oheight; % depth= 4/9 height; % depth+height=12; % capheight=3/2 oheight; % sharp=.5; nwidth= 3.14159/4; % roman=0;italic=1; style=italic; % penwidth= 1/5 oheight; % penheight= 1/6 penwidth; % expen=0; % serifsize= 2/5 penwidth; % footsize = 2/5 penwidth; % penangle = 45; % slant=.15; % oslant=.25; % mode=datadisk=font=1+proof=1+crs; % input pen0.mf; % input pen.mf; if mode=font: ppp=3.6; fntmode; tfxmode; maxht height*ppp+1; trxy slant; texinfo slant, 5/6 owidth, 1/3 owidth, 1/3 owidth, oheight, 0,0; else: ppp=200/height; fi; trxy slant; if mode=proof: proofmode; fi; if mode=datadisk: drawdisplay; if (mode-proof)(mode-font)(mode-crs) ≠ 0: trxx .90; fi; fi; % The value of PPP is the number of pixels per point. On the XGP % there are 3.6 pixels per point. For datadisk and proofmode, % PPP is fudged so that TOPLINE has a y-coordinate of 200 pixels. w1 = penwidth*ppp; w0=penheight*ppp. % w1 and w0 are the width and height of the pen in pixels. Using them % and PENANGLE we define the pen: no penreset; subroutine setpen(var penangle): spen(4*((cosd penangle)*(cosd penangle)/(w1*w1)+ (sind penangle)*(sind penangle)/(w0*w0)), 4*(sind(2*penangle))*(1/(w1*w1)-1/(w0*w0)), 4*((sind penangle)*(sind penangle)/(w1*w1)+ (cosd penangle)*(cosd penangle)/(w0*w0)), 0,0,0,0). if expen=0: call setpen(penangle); spen;; else: call normpen; epen;; fi; minvr 0.25; minvs 0.25; % Now define the following variables: % % TP The y-coordinate in pixels of the center of a pen whose % top is HEIGHT points above the baseline. % MID The y-coordinate in pixels of the center of a pen whose % top is oheight points above the baseline. % BASE The y-coordinate in pixels of the center of a pen whose % bottom is just touching the baseline. % BT The y-coordinate in pixels of the center of a pen whose % bottom is DEPTH points below tha baseline. % LEFT The x-coordinate in pixels of the center of a pen whose % left edge is just touching the line x=0, which is the % left-most edge of "o". % RIGHT The x-coordinate in pixels of the center of a pen whose % right edge is OWIDTH points to the right of the line x=0. % CAP The y-coordinate in pixels of the center of a pen whose % top edge is CAPHEIGHT points above the baseline. % SRAD SRAD pixels = SERIFSIZE points. % SSRAD SSRAD = 2/3 SRAD; for smaller serifs. % SSSRAD SSSRAD = 2/3 SSRAD; for still smaller serifs. % SPACE The amount of blank space inserted at the left and right % of characters by default. % ICX amount to increment italic correction when upper-right % "corner" of char is circle. top1tp = round(height*ppp); top1mid = round(oheight*ppp); bot1base = 0; bot1bt = - round(depth*ppp); lft1left = 0; rt1right = round(owidth*ppp); srad = serifsize * ppp; 9/4 sssrad = 3/2 ssrad = srad; top1cap=round(capheight*ppp); 2 space = nwidth (right - left); icx=1/4[base,mid]; % The shapes of most letters are defined in terms of a few important % points, which are now defined. Assume for the moment that % owidth=oheight. Let C be the circle inscribed in the square formed % by the lines y=base, y=mid, x=left, and x=right. % This is the circle in which the center of the pen moves to draw % an o. Let the right, top, left, and bottom extreme points of % this circle be points 3, 12, 9, and 6 (numbering them with % their clock-directions.) % The two vertical strokes of n are determined by the facts % that the distance % between their centers is NWIDTH*(RIGHT-LEFT) and that they % have the same distance from the center of the o. % They intersect the circle C in four points, which will % be called points 2, 10, 8, and 4, again numbering them by their % clock-directions. % These eight points are used frequently in drawing the lower-case % alphabet. For example, the curved part of "d" is the arc through % the points 2,12,10,9,8,6,4, and the vertical part of the d is the % line segment through points 2 and 4 with endpoints intersecting % the lines y=tp and y=base. Because of their general utility these % points will be defined each time a letter is drawn, even though % not all of them are used for every letter. % To draw the circular arcs it is necessary to know the tangent % direction of the circle at the points 10,8,4,2. The variables % tenx and teny will be set so that (tenx, teny) is the tangent % direction at point 10 going clockwise. This is the % same as the tangent direction at 4 going counter-clockwise. From all % that has been said so far you would think that the tangent % direction at 2 going clockwise would be (tenx, -teny), but % when the complications described below for italic fonts are % taken into account, you will find that the slope at 2 is % not determined from the slope at 10, so the variables % twox and twoy are set so that (twox, twoy) is the slope % at 2 going clockwise. The slope at 8 going counter-clockwise % is (twox,twoy). % The midpoints of the segments 8-10 and 2-4 are often useful, % so they will always be defined, too. They are close to the % points 9 and 3, so they will be numbered 90 and 30. As an example % of their use, consider drawing the arch and right stroke of n. % Suppose that point 20 has been defined as the bottom of the % right stroke. The obvious way would be with the two statements: % % draw 10{tenx,teny}..12{1,0}..2{twox,twoy}; draw 2..20; % % But this would make an ugly sharp angle at 2. Instead use: % % draw 10{tenx,teny}..12{1,0}..30{0,-1}..20; % % Similarly the point 90 is used in the transistion from the left % stroke of u to the bottom stroke. % % Actually the points 90 and 30 are the exact midpoints % of the segments 8..10 and 2..4 only if the variable SHARP has % the value .5. If SHARP has the value 1, 90 is identical to 8 % and 30 is identical to 2, causing the curve in n and u to make % a sharper turn. In general SHARP is the quotient of the length % of the segment 30..4 by the length of the segment 2..4, and also % the quotient of the length of the segment 90..10 by the length of % the segment 8..10. That is, as SHARP increases, 30 goes up and % 90 goes down. % % The reason I am typing so many comments instead of getting on % with computing the y-coordinates of the points 10,2,8, and 4 % is that its harder to do so than you might think, and the reason % its hard is that if OSLANT is non-zero, some complications % occur. The problem with the above construction is that it % produces roman letters, not italic ones. You can slant everything % by setting SLANT=.15, and set STYLE=ITALIC to get italic-style % a and f, but all you get is slanted roman with italic a and f. % What I think will make the font look italic is if the circle % of the o is slanted more than the whole font. That is, imagine % that the points 12,10,9,8,6,4,3,2 are constructed as follows: % "Slant" the circle of the o so that it becomes an elipse leaning % to the right that is internally tangent to the rectangle % formed from the four lines x=left,x=right, y=base, y=mid. % Draw the uprights of n as before. Then the extreme points % of the elipse and the intersections of the elipse with the % uprights give the eight points. This produces % a very different result than slanting the whole diagram. % So let's do it. Consider the transformation: % % T(x, y)= (A x + B y, y) % % Given that T(x, y) is on the circle x↑2 + y↑2 = r↑2, write % y in terms of x. We have: % % A↑2 x↑2 + 2 A B x y + (B↑2 + 1) y↑2 = r↑2 % % or (B↑2 + 1) y↑2 + (2 A B x) y + (A↑2 x↑2 - r↑2) = 0. % % Now suppose that A↑2 = 1 + B↑2, and solve for y: % % B x +/- sqrt(r↑2 - x↑2) % y= ------------------------- (1) % A . % % The extreme points of the solution set in the x-direction have % x = -r and x = +r. Obviously the extreme points in y direction % have y = r and y = -r also, since the transformation doesn't change % y. So the solution set is an elipse internally tangent to the % square max(|x|,|y|) = r. If B is zero, the solution set is just % the circle x↑2+y↑2=r↑2. As B becomes very large, the term B x / A % dominates the term sqrt(r↑2-x↑2)/A, and the solution set becomes % close to the line y=x. Thus if we let B=OSLANT, the solution % set of (1) is the elipse which we are to intersect with the % uprights of the n. % % When x = -r, y = - B r / A. % % Similarly when y = -r, x = - B r / A. % % These formulas give the y-coordinate of points 9 and 3, and the % x-coordinate of points 12 and 6. The y-coordinates of points % 2, 4, 10 and 8 are determined by (1). % % Points 10 and 2 are on the positive branch of the solution for y, % and the slopes at points 10 and 2 is are given by differentiating % (1), assuming the larger root is taken: % % y' = (1 / A) (B - x / sqrt(r↑2-x↑2)). % % We are finally ready to put it all together. We can't define % the points themselves since point definitions are local to % their section, so we define the values tenx, teny, twox, twoy, % which give the slopes at points ten and two, sixx and niney % which are the x and y coordinate of points 6 and 9, respectively, % eightx, eighty, fourx, foury, which are similarly the coordinates % of points 8 and 4. We use exszero and vvyezero for the coordinates % of the center of the circle. All of these values depend on right, left, % mid, base, oslant, and nwidth. It is convenient to write a subroutine % called SETCIRCLE % that sets the values, given the values of right, left, mid, and base; % then the subroutine can be used for defining the clock points on % the circle of the upper-case "O" as well as the lower-case "O". % OSLANT and NWIDTH could be changed between calls to SETCIRCLE, % but this is unusual, so they will not be arguments. subroutine SETCIRCLE(var left, var right, var mid, var base): new tenx, teny, twox, twoy, sixx, niney, fourx, foury, eightx, eighty, B, A, rx, ry, exszero, vvyezero, cleft, cright, ctop, cbot; cleft=left; cright=right; ctop=mid; cbot=base; fourx - eightx = nwidth (right - left); eightx - left = right - fourx; B = oslant; A = sqrt(1 + B B); rx = 1 / 2 (right - left); ry = 1 / 2 (mid - base); % remember that because RIGHTLINE may be # MIDLINE; the final % "circle" may be distorted. Use rx and ry as the two radii of % the final "circle". exszero = 1/2[left,right]; vvyezero = 1/2[base,mid]; niney - vvyezero = - ry B / A; sixx - exszero = - rx B / A; eighty - vvyezero = - ry (1/A) (sqrt(1 - NWIDTH NWIDTH) + B NWIDTH); foury - vvyezero = ry (1/A) (B NWIDTH - sqrt(1 - NWIDTH NWIDTH)); twox = tenx = rx/ry; twoy = (1/A) (B - NWIDTH / sqrt(1 - NWIDTH NWIDTH)); teny = (1/A) (B + NWIDTH / sqrt(1 - NWIDTH NWIDTH)). % The following subroutine, CIRCLE, is called at the beginning % of every letter like this: % % call CIRCLE(1,2,3,4,5,6,7,8,9,10,11,12); % % it defines the points in terms of the variables set by SETCIRCLE. % Notice that points 1,5,7,11 have creeped in; they are not on % the circle at all, but are often useful: point 11 is the top of % the left upright of the n, points 7 and 5 are the bottom of the % uprights of the n, etc. subroutine circle(index a, index b, index c, index d, index e, index f, index g, index h, index i, index j, index k, index l): yf=cbot; xf=sixx; xi=cleft; yi=niney; yl=ctop; xl - exszero = exszero - xf; xc = cright; yc - vvyezero = vvyezero - yi; nwidth * rx = exszero - xh = xd - exszero; xh=xj; xb=xd; yh = eighty; yd = foury; ctop - yj = foury - cbot; ctop - yb = eighty - cbot; xa=xb=xe; ya=ctop; ye=cbot; xk=xg=xh; yk=ctop; yg=cbot. subroutine lserif(index i, var dx, var dy, var m, var theta): % move pen in a circle, starting at point i in the direction % (dx,dy), curving to the left so that the circle becomes % tangent to the line y=m; continue past the point of tangency % for theta more degrees. % let point 1 be the intersection of the line y=m and the line % through i in the direction dx,dy: y1=m; new alpha; y1-yi=alpha dy; x1-xi=alpha dx; new d; % set d to the distance between point i and point 1: d = sqrt((xi-x1)(xi-x1) + (yi-y1)(yi-y1)); % let point 2 be the point on the line y=m with distance % d from point 1 that is to the left of the line through i in % the direction (dx,dy): if alpha*(yi-m)<0: x2=x1-d; else: x2=x1+d; fi; y2=m; % Now let point 3 be the center of the circle of motion: new alpha; x3=x2; x3=xi-alpha dy; y3=yi+alpha dx; % now draw the arc from i to 2; draw i{dx,dy}..2{yi-m,0}; % now we must draw an arc around point 3 starting at point 2 % for a distance of theta degrees. The terminal point of % the arc is (x3 - (sind theta)(y2-y3), y3 + (cosd theta)(y2-y3)), % and the slope at the terminal point is (-cosd theta, -sind theta) % if yi<m and (cosd theta, sind theta) if yi>m. x4=x3 - (sind theta)(y2-y3); y4=y3+(cosd theta)(y2-y3); draw 2{yi-m,0}..4{(yi-m)(cosd theta), (yi-m)(sind theta)}. subroutine rserif(index i, var dx, var dy, var m, var theta): % like rserif, but move to the right instead of to the left. y1=m; new alpha; y1-yi=alpha dy; x1-xi=alpha dx; new d; d = sqrt((xi-x1)(xi-x1) + (yi-y1)(yi-y1)); if alpha*(yi-m)<0: x2=x1+d; else: x2=x1-d; fi; y2=m; % Now let point 3 be the center of the circle of motion: new alpha; x3=x2; x3=xi+alpha dy; y3=yi-alpha dx; % now draw the arc from i to 2; draw i{dx,dy}..2{m-yi,0}; x4=x3 + (sind theta)(y2-y3); y4=y3+(cosd theta)(y2-y3); draw 2{m-yi,0}..4{(yi-m)(- cosd theta), (yi-m)(sind theta)}. subroutine charbegin(var code, var l, var r, var d, var ic): % prepare for character with code CODE, whose left and right % strokes are centered at l and r respectively. d is the % depth in points. ic is the y-coordinate of the highest point % which is extreme in the x-direction; for example for L ic is 0, % for T ic is tp, for o ic is the y-coordinate of circle-point 3. % Draw guidelines in proof mode and datadisk mode, set character % parameters in font mode. In any case, set incx so that the % character becomes centered in its box. charcode code; new charwidth; charwidth = (r-l) + 2 space; % charwidth in pixels no proofmode; if mode=font: charwd charwidth/ppp; charht height; chardp b; charic slant*(top1ic)/ppp; chardw charwidth; incx space - l; fi; % draw guidelines if mode is debug or proof: if (mode - datadisk)*(mode - proof) = 0: trxy 0; x1=x2=x3=x4=l-space; x10=x20=x30=x40=r+space; y1=y10=y100=height*ppp; y2=y20=oheight*ppp; y3=y30=0; y4=y40=y400= - depth*ppp; x100=x400=rt1r+slant*ic; cpen; 1 draw 1..4; draw 10..40; draw 1..10; draw 2..20; draw 3..30; draw 4..40; draw 100..400; if expen=0: spen;; else: call normpen; fi; trxy slant; fi. subroutine lcircle(index i, var dx, var dy, var theta): % move pen in circle, from point i, starting in direction {dx,dy}, % curving to the left in a circle of radius sqrt(dx↑2+dy↑2), for % theta degrees. new s,c; s=sind theta; c=cosd theta; %point 1 will be terminus of path. x1 = xi - dy + dy c + dx s; y1 = yi + dx + dy s - dx c; draw i{dx,dy}..1{dx c - dy s, dx s + dy c}. subroutine rcircle(index i, var dx, var dy, var theta): % like lcircle, but curve to the right instead of to the left: new s,c; s=sind theta; c=cosd theta; x1 = xi + dy - dy c + dx s; y1 = yi - dx + dy s + dx c; draw i{dx,dy}..1{dx c + dy s, - dx s + dy c}. subroutine collinear(index i, index j, index k): % assert that points i, j, and k are collinear. % The values xj, xk, yj, yk must already be determined. if xk = xj: xi=xj; else: if yk=yj: yi=yj; else: (xi - xj) / (xk - xj) = (yi - yj) / (yk - yj); fi; fi. subroutine lfoot(index i): no proofmode; y1=yi; x1=xi-footsize*ppp/2; x2= 2[xi,x1]; y2= y1 - (sind penangle) footsize*ppp/4; draw i..1{-1,0}..2{-cosd penangle, - sind penangle}. subroutine rfoot(index i): no proofmode; y1=yi; x1=xi+footsize*ppp/2; x2= 2[xi,x1]; y2= y1 + (sind penangle) footsize*ppp/4; draw i..1{1,0}..2{cosd penangle, sind penangle}.