<< . .

. 23
( : 45)



. . >>

EXERCISE 22.1 message command
message command
Under what circumstances is (a) ASCII char n = n? (b) char ASCII s = s?
message op
message
EXERCISE 22.2 errmessage
Why are there primitive operations to convert from strings to numbers assum- errhelp
help
ing octal notation and hexadecimal notation, but not assuming decimal notation?
scantokens
mode def
EXERCISE 22.3
ALINGHAM
Write an octal macro that converts a nonnegative integer to an octal string. THOMSON

A message command allows you to communicate directly or indirectly with
the user. It has the general syntax
message command ’’ message op string expression
message op ’’ message | errmessage | errhelp
If you say ˜message s™, the characters of s will be typed on the terminal, at the
beginning of a new line; ˜errmessage s™ is similar, but the string will be preceded by
"! " and followed by ".", followed by lines of context as in ™s normal error
messages. If the user asks for help after an errmessage error, the most recent errhelp
string will be typed (unless it was empty).
doesn™t allow you to have an array of di¬erent macros m[i];
but you can have an array of strings that have macro-like behavior, via
scantokens. The mode def construction of Appendix B exploits this idea.




Many other useful Practises
mecanicks perform by this Theo.
as the ¬nding the length of strings.
” WILLIAM ALINGHAM, Geometry Epitomized (1695)

Forgive me, if my trembling Pen displays
What never yet was sung in mortal Lays.
But how shall I attempt such arduous String?
” JAMES THOMSON, The Castle of Indolence (1748)
(page 190)




23
Online
Displays
Chapter 23: Online Displays 191


How do you get pictures to appear on your screen? Plain provides showit
currentpicture
the ˜showit™ command, which displays the currentpicture . Furthermore you can screenchars
ask for ˜screenchars™; this automatically does a showit at the time of each endchar
screenstrokes
endchar. And you can see all the action by asking for ˜screenstrokes™; this display command
automatically does a showit after every draw or ¬ll. display
inwindow
window
The above-described features of plain are implemented from low-
openwindow command
level primitive commands, by macros that appear in Appendix B. At the low- openwindow
obeys commands such as ˜display currentpicture inwindow 1™;
est level, window spec
at
there™s also an ˜openwindow™ command that de¬nes a correspondence between -
screen place
coordinates and screen coordinates. The syntax is from
to
display command ’’ display picture variable inwindow window screen coordinates
window ’’ numeric expression Cartesian
openwindow command ’’ openwindow window window spec
window spec ’’ screen place at pair expression
screen place ’’ from screen coordinates to screen coordinates
screen coordinates ’’ pair expression
A window is an integer between 0 and 15, inclusive; it represents one of sixteen “win-
dows” or “portholes” that provides between its pictures and the outside
world. The window mentioned in a display command must previously have been
“opened” by an openwindow command.
™s windows should not be confused with the so-called windows
provided by many modern operating systems. If you have such a system,
you™ll probably ¬nd that all of ™s pictorial output appears in one operating-
system window, and all of its terminal I/O appears in another, and you might be
running other jobs (like the system editor) in another. ™s windows are not
so fancy as this; they are just internal subwindows of one big picture window.
The command ˜openwindow k from (r0 , c0 ) to (r1 , c1 ) at (x, y)™ associates a
rectangular area of the user™s screen (or of the user™s big picture window) with
pixels in ™s coordinate system. All of the numbers in this command (namely
k, r0 , c0 , r1 , c1 , x, and y) are rounded to the nearest integer if they aren™t integers
already. Furthermore r0 is replaced by max(0, min(maxr , r0 )) and r1 is replaced by
max(r0 , min(maxr , r1 )), where maxr is the maximum number of rows on the screen;
similar adjustments are made to c0 and c1 . The two (r, c) values are row and column
numbers on the screen; the topmost row is conventionally taken to be row zero, and the
leftmost column is taken to be column zero. (These conventions for screen coordinates
are quite di¬erent from the normal Cartesian coordinate system used everywhere else in
, but somehow they seem appropriate when applied to screens.) Point (x, y)
of ™s raster will be equated to the upper left corner of the rectangle, i.e., to
the upper left corner of the pixel in screen column c0 of screen row r0 . The window
itself occupies r1 ’ r0 rows and c1 ’ c0 columns. It follows that the pixel in column c1
of row r1 is not in the window itself, but it is the screen pixel diagonally just below
and to the right of the lower right corner of the window.
EXERCISE 23.1
What are the coordinates of the boundary of such a window?
192 Chapter 23: Online Displays


If you run on a system that doesn™t support general bitmap dis- nullpicture
blankpicture
plays, the display and openwindow commands will do nothing. You™ll have
currentwindow
to look at hardcopy output, o¬line. (But your might run a bit faster.) meta-characters
Knuth
The syntax for display insists that you display a picture variable , not a a
picture expression ; thus, you can™t ˜display nullpicture™. Plain -
de¬nes a special variable blankpicture that™s entirely blank, just so that you can
easily display nothing whenever you like.

A window may be opened any number of times, hence moved to di¬erent
locations on the screen. Opening a window blanks the corresponding screen
rectangle as if you had displayed blankpicture .

The e¬ect of overlapping windows is unde¬ned, because does not
always repaint pixels that have remained unchanged between displays.

Changes to a picture do not change the displays that were generated from it,
until you give another display command explicitly. Thus, the images embla-
zoned on your screen might not exist any longer in ™s picture memory.

has an ˜openit™ macro that opens currentwindow ; this vari-
Plain
able currentwindow is always zero unless you change it yourself. The showit
macro displays currentpicture in currentwindow ; and it™s also designed to call openit”
but only the very ¬rst time showit is invoked. This means that the screen normally
won™t be touched until the moment you ¬rst try to display something.

Appendix E explains how to manage a more elaborate scheme in which six
windows can be used to show how meta-characters vary under six di¬erent
font-parameter settings. The author used such a six-window system when developing
the Computer Modern typefaces; here is a typical example of what appeared on his
terminal when the letter ˜a™ was being re¬ned:




(Figure 23 will be inserted here; too bad you can™t see it now.)
Chapter 23: Online Displays 193


EXERCISE 23.2 new window
screen cols
The openit macro in Appendix B speci¬es (’50, 300) as the upper left corner
VENEZKY
point of the window used for showing all the pictures. This might clip o¬ the bottom STRAVINSKY
of a large character, if your screen is limited to, say, 360 rows. How could you change
openit so that the character images will be raised 20 rows higher than they would be
in the standard setting?
EXERCISE 23.3
Design a ˜new window™ routine that allocates windows 1, 2, . . . , 15. If the
user says ˜new_window $(u,v)™, where $ is any su¬x and u,v are pairs of coordinates
for two opposite corners of a rectangle, your macro should map that rectangle to the
next available screen rectangle and open it as window number window$. The allocation
should be left to right, top to bottom; assume that the screen is an in¬nite rectangle,
screen cols wide.




Editing will be done on-line with a display scope and keyboard.
” RICHARD L. VENEZKY, in American Documentation (1968)

In future I might be obliged to turn for material to the tube.
” IGOR STRAVINSKY, in Harper™s (1970)
(page 194)




24
Discreteness
and Discretion
Chapter 24: Discreteness and Discretion 195


Pixel patterns are indistinguishable from continuous curves, when the pixels are luxo
rounding
small enough. After all, the human eye is composed of discrete receptors, and hand-tuning
visible light has a ¬nite wavelength. Our hypothetical luxo printer of Chapter 11, raster
autorounding
with its resolution of 2000 pixels per inch, would surely be able to produce printed smoothing
pages of high quality, if it existed; the physical properties of ink would smooth digitizing
out all the tiny bumps, obliterating all the evidence that the letterforms had
been digitized. However, it will always be less expensive to work with devices
of lower resolution, and we want the output of to look as good as
possible on the machines that we can a¬ord to buy. The purpose of this chapter
is to discuss the principles of “discreet rounding,” i.e., to consider the tasteful
application of mathematical techniques by which can be made to
produce satisfactory shapes even when the resolution is rather coarse.
The technical material in this chapter is entirely marked with danger
signs, since careful rounding tends to make programs more complex;
a novice user will not wish to worry about such details. On the other hand,
an expert er will take pains to round things properly even when
preparing high-resolution fonts, since the subtle re¬nements we are about to
discuss will often lead to signi¬cantly better letterforms.
We should realize before we begin that it would be a mistake to set our
hopes too high. Mechanically generated letters that are untouched by human
hands and unseen by human eyes can never be expected to compete with al-
phabets that are carefully crafted to look best on a particular device. There™s
no substitute for actually looking at the letters and changing their pixels until
the result looks right. Therefore our goal should not be to make hand-tuning
obsolete; it should rather be to make hand-tuning tolerable. Let us try to create
meta-designs so that we would never want to change more than a few pixels
per character, say half a dozen, regardless of the resolution. At low resolutions,
six pixels will of course be a signi¬cant percentage of the whole, and at higher
resolutions six well-considered pixel changes can still lead to worthwhile improve-
ments. The point is that if our design comes close enough, a person with a good
bitmap-editing program will be able to optimize an entire font in less than an
hour. This is an attainable goal, if rounding is done judiciously.
tries to adjust curves automatically, so that they are well adapted
to the raster, if the internal quantities autorounding and/or smoothing have
sets autorounding := 2 and smoothing := 1, so you
positive values. (Plain
generally get these features unless you turn them o¬ yourself.) But all the examples in
this chapter will be generated with autorounding := smoothing := 0 unless otherwise
mentioned, because this will keep ™s automatic mechanisms from interfering
with our experiments. We shall discuss the pros and cons of automatic rounding after
we have explored the general problem in more detail.
The ¬rst thing we need to understand about rounding is ™s pro-
cedure for digitizing a path. A path of length n can be regarded as a trajec-
tory z(t) that is traced out as t varies from 0 to n. In these terms, the corresponding
digitized path is most easily described by the formula ˜round z(t)™ for 0 ¤ t ¤ n; each
196 Chapter 24: Discreteness and Discretion


z(t) is rounded to the nearest point with integer coordinates. For example, if a path lowres
round
goes through point (3.1, 5.7), its digitization will go through point (3, 6). The digitized
trajectory makes discrete jumps at certain values of t, when round z(t) hops from one
point to another; the two points will be one pixel apart, and we can imagine that the
digitized path traverses the horizontal or vertical edge between them when it jumps.
When an ordinary region is being ¬lled, this rule for digitizing paths boils
down to a simple criterion that™s easy to visualize: A pixel belongs to the
digitized region if and only if its center point lies inside the original undigitized path.
For example, two versions of Chapter 5™s Ionian ˜ ™ are shown here at a resolution of
200 pixels per inch, using the characteristics of lowres mode in Appendix B:




(Figure 24a&b will be inserted here; too bad you can™t see it now.)




The heavy broken lines are digitized paths, and the pixels inside these ragged bound-
aries are those whose centers lie in the shaded regions.
The ˜ ™ on the left has digitized well; but the one on the right has problems,
because it was based on curves that were generated without taking the raster
into account. The di¬erence between these two letters is entirely due to line 8 of the
program in Chapter 5, which says
curve sidebar = round 1/18em ;
this equation determines the position of the leftmost and rightmost edges of the ˜ ™
before digitization, and it leads to the nice digitized form in the left-hand example.
Without the word ˜round™, we get the inferior right-hand example, which was obtained
program except that curve sidebar was set to 1/18em
by exactly the same
exactly. One little token”which changed an exact calculation to an approximate,
rounded calculation”made all the di¬erence!
Curves that are placed in arbitrary positions on a raster can lead to digital
disasters, even though the curves themselves aren™t bad. For example, suppose
we take the right-hand example above and shift it just 0.05 and 0.10 pixels to the right:




(Figure 24c&d will be inserted here; too bad you can™t see it now.)
Chapter 24: Discreteness and Discretion 197


The ¬rst shift of 0.05 pixels causes a tiny pimple to appear at the right edge; after an- pimple
¬‚at
other small shift the pimple has grown into a mole, and the left edge has become too ¬‚at.
de¬ne corrected pixels
overshoot
A designer who is asked to make a digital ˜O™ that is 22 pixels wide will beginchar
certainly have pixels in mind when making the design. Therefore it™s not
surprising that our program to generate a digital ˜O™ should pay attention to actual
pixel positions by rounding curve sidebar as in this example. We have distorted the
in¬nite-resolution curve slightly so that it will digitize well, before digitizing it.
A path z(t) will digitize well if the digitization process doesn™t change it too
much; thus, we want z(t) to be essentially the same as round z(t), at all the
important places. But what places are “important”? Experience shows that the most
critical points are those where the path travels horizontally or vertically, i.e., where it
runs parallel to the raster lines. It™s best to arrange things so that a curve becomes
parallel to the raster lines just when it touches or nearly touches those lines; then it
will appear to have the right curvature after digitization. The worst case occurs when
a curve becomes parallel to the raster just when it™s halfway between raster lines; then
it gets a pimple or a ¬‚at spot.
Diagonal slopes, where a curve has a ±45—¦ tangent angle, are also potential
sources of unwanted pimples and ¬‚ats. Similarly, at higher resolutions it is
sometimes possible to detect small glitches when a curve travels with slopes of ±1/2 or
±2/1. Rational slopes m/n where m and n are small integers turn out to be somewhat
dangerous. But diagonals are of secondary importance; horizontal and vertical slopes
lead to more severe problems.
These considerations suggest a simple general principle for adapting the out-
lines of shapes to be digitized: If you know that the outline will have a vertical
tangent at some point, round the x coordinate to an integer and leave the y coordinate
unchanged. If you know that the outline will have a horizontal tangent at some point,
round the y coordinate to an integer and leave the x coordinate unchanged.
Incidentally, the horizontal tangent points in our ˜ ™ examples were taken
care of by the fact that ˜de¬ne corrected pixels™ makes the overshoot pa-
rameter o nearly an integer, together with the fact that beginchar makes h an integer.
If the y coordinates had not been rounded at the horizontal tangent points, our bad
examples would have looked even worse.
Before we go further into the study of rounding, we had better face up to a
technicality that™s sometimes important: We said that the pixels of a digi-
tized region are those whose centers lie inside the undigitized region; but this rule is
vague about what happens when the centers happen to fall precisely on the undigitized
boundary. Similarly, when we said that round z(t) jumps from one point to an adjacent
point, we ignored the fact that a curve such as z(t) = (t, t) actually jumps from (0, 0)
to (1, 1) when it is rounded as t passes 1/2; those points are not adjacent.
skirts both of these problems in an interesting way: It shifts all of its paths to the right
by an in¬nitesimal amount δ, and it also shifts them upward by an even smaller in-
¬nitesimal amount δ , so that no path actually touches a pixel center. Here δ and are
positive numbers that are chosen to be so small that their actual values don™t matter.
For example, the path z(t) = (t, t) becomes (t + δ, t + δ ), which jumps from (0, 0) to
(1, 0) to (1, 1) because it momentarily rounds to (1, 0) when t = 1/2 ’ 2δ .
198 Chapter 24: Discreteness and Discretion


Points of the form (m + 1/2, n + 1/2), where m and n are integers, lie in the ambiguous point
draw
centers of their pixels. They are called “ambiguous” points because we can™t
pencircle
round them to the nearest integer neighbor without deciding which of four adjacent good.x
points is to be considered the nearest. If we imagine taking a curved outline and shifting good.y
gumdrop
it slowly to the right, the digitized image makes abrupt transitions when the outline
passes over an ambiguous point. When a path comes near an ambiguous point, the
path is farthest away from its digitization. Thus the ambiguous points are points of
instability, and digitizing works best when paths don™t get too close to them.
Let™s consider now what happens when we draw with a pen, instead of ¬lling
an outline. It may seem that the simplest possible draw command would be
something like this:
pickup pencircle; draw (0, 0) . . (10, 0);
what could be easier? But a closer look shows that this is actually about the worst case
that could be imagined! A circular pen of diameter 1 that goes from (0, 0) to (10, 0) has
upper and lower boundaries that go from (0, ±1/2) to (10, ±1/2), and both of these
boundaries run smack through lots of ambiguous points. has to decide
whether to ¬ll the row of pixels with 0 ¤ y ¤ 1 or the lower row with ’1 ¤ y ¤ 0,
neither of which is centered on the given line. According to the rule stated earlier,
shifts the path very slightly to the right and very, very slightly up; thus
the pixels actually ¬lled are bounded by (0, 0) - - (10, 0) - - (10, 1) - - (0, 1) - - cycle.
EXERCISE 24.1
Continuing this example, what pixels would have been ¬lled if the path had
been ˜(0, 0) . . (10, ’epsilon )™ ?
In general when we draw with a ¬xed pen, good digitizations depend on
where the edges of the pen happen to fall, not on the path followed by the
pen™s center. Thus, for example, if the path we™re drawing has a vertical tangent at
point z1 , we don™t necessarily want x1 to be an integer; we want lft x1 and rt x1 to
be integers. If there™s a horizontal tangent at z2 , we want top y2 and bot y2 to be
integers. The pens created by pencircle always have the property that (lft x) ’ (rt x)
and (top y) ’ (bot y) are integers; hence both edges will be in good or bad positions
simultaneously.
Suppose that we want x1 to be approximately equal to ±, and we also want
it to be at a good place for vertical tangents with respect to the pen that has
currently been picked up. One way to de¬ne x1 is to say
lft x1 = round(lft ±);
this does the right thing, because it makes lft x1 an integer and it also makes x1 ≈ ±.
Similarly, to make y2 ≈ β good for horizontal tangents, we can say
top y2 = round(top β).
Such operations occur frequently in practice, so plain provides convenient
abbreviations: We can say simply
x1 = good.x ±; y2 = good.y β
instead of using indirect equations for lft x1 and top y2 .
Chapter 24: Discreteness and Discretion 199


Let™s look one last time at the letters of the logo, in order to make logo.mf
xgap
them round properly. Chapter 11 describes a ¬le logo.mf that draws the seven
ygap
characters, but we can improve the results by making pixel-oriented re¬nements. In leftstemloc
the ¬rst place, we can replace the command barheight
de¬ne pixels
de¬ne whole pixels
de¬ne pixels(s, u, xgap , ygap , leftstemloc , barheight ) de¬ne good x pixels
de¬ne good y pixels
by something better: Looking at the uses of these ad hoc dimensions, we see that xgap O
T
and ygap ought to be integers; leftstemloc should be a good.x value for logo pen ; and
beginchar
barheight should be a good.y value. Therefore we say change width

de¬ne pixels(s, u);
whole pixels(xgap , ygap );
de¬ne
de¬ne good x pixels(leftstemloc );
de¬ne good y pixels(barheight );

, will do the right thing. (The logo pen
these commands, provided by plain
should be picked up before the last two commands are given.) These few changes, and
a change to the ˜ ™, su¬ce to ¬x all the letters except ˜ ™.

EXERCISE 24.2
The program for ™s ˜ ™ appears in Chapter 18. What changes
would you suggest to make it digitize well?

The ˜ ™ presents a new problem, because we want it to be symmetric between

<< . .

. 23
( : 45)



. . >>