summaryrefslogtreecommitdiff
path: root/fonts/bbold/bbbase.mf
blob: dd1f3c633b0c1d60c9bc2f1a0cc65bdbcbdf401a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
% 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.