ńņš. 8 |

The four āmode commandsā control the amount of interaction during error recovery,

just as in TEX. A job starts in errorstopmode, and you can also resurrect this mode

by interrupting ; scrollmode, nonstopmode, and batchmode are the

220 Chapter 26: Summary of the Language

modes you get into by hitting ā˜Sā™, ā˜Rā™, or ā˜Qā™, respectively, in response to error messages picture command

addto command

(cf. Chapter 5).

addto

also

picture command ā’ā’ addto command | cull command addto

addto command ā’ā’ addto picture variable also picture expression contour

| addto picture variable contour path expression with list addto

doublepath

| addto picture variable doublepath path expression with list with list

with list ā’ā’ empty | with list with clause with clause

with clause ā’ā’ withpen pen expression | withweight numeric expression withpen

withweight

cull command ā’ā’ cull picture variable keep or drop pair expression cull command

| cull command withweight numeric expression cull

withweight

keep or drop ā’ā’ keeping | dropping keep or drop

keeping

The addto and cull commands are the principal means of making changes to pictures; dropping

they are discussed fully in Chapter 13. display command

display

display command ā’ā’ display picture variable inwindow window inwindow

window

window ā’ā’ numeric expression openwindow command

openwindow command ā’ā’ openwindow window window spec openwindow

window spec ā’ā’ screen place at pair expression window spec

at

screen place ā’ā’ from screen coordinates to screen coordinates screen place

screen coordinates ā’ā’ pair expression from

to

screen coordinates

Chapter 23 explains how to display stuļ¬ on your screen via display and openwindow.

shipout command

shipout command ā’ā’ shipout picture expression shipout

generic font

prooļ¬ng

You may have wondered how actually gets pictorial information into a xoļ¬set

font. Here at last is the answer: ā˜shipout vā™ puts the pixels of positive weight, as yoļ¬set

charcode

deļ¬ned by the picture expression v, into a generic font output ļ¬le, where they will be

charext

the bitmap image associated with character number charcode mod 256 + charext ā— 256. charwd

The pixels of v are shifted by (xoļ¬set , yoļ¬set ) as they are shipped out. (However, no charht

chardp

output is done if prooļ¬ng < 0. The values of xoļ¬set , yoļ¬set , charcode , and charext are charic

ļ¬rst rounded to integers, if necessary.) This command also saves the values of charwd , chardx

font metric information

charht , chardp , charic , chardx , and chardy ; they will be associated with the current

special command

charcode when font metric information is produced. (See Appendices F and G for the special

basic principles of font metric information and generic font ļ¬les.) numspecial

special command ā’ā’ special string expression

| numspecial numeric expression

The special and numspecial commands send alphabetic and numeric information

to the generic font output ļ¬le, if prooļ¬ng is nonnegative. For example, the labels on

proofsheets are speciļ¬ed in this way by macros of plain . Appendices G

and H provide further details.

We have now discussed every kind of command but one; and the remaining one

is even more special than the special command , so we had better defer its discussion to

an appendix. Appendix F will complete the syntax by deļ¬ning font metric command .

For now, we merely need to know that font metric commands specify fussy font facts;

examples are the kerning and ā˜font normal spaceā™ statements in the logo

program of Chapter 11.

Chapter 26: Summary of the Language 221

And thereā™s one more loose end to tie up, going back to the very ļ¬rst syntax dump

end

rule in this chapter: The token ā˜dumpā™ can be substituted for ā˜endā™, if a special version

INIMF

of called ā˜INIMFā™ is being used. This writes a ļ¬le containing the macros outer

deļ¬ned so far, together with the current values of variables and the current meanings delimiters

showtoken

of symbolic tokens, so that they can be loaded as a base ļ¬le. (It is analogous to TEXā™s THOREAU

\dump command.) Base ļ¬les are discussed at the end of Appendix B. truth

ZAPF

EXERCISE 26.1

Run with the input

\newinternal a;

let b=a; outer a,b,c;

let c=b; delimiters a::;

showtoken a,b,c; end

and explain the computerā™s responses.

Our life is frittered away by detail.

An honest man has hardly need

to count more than his ten ļ¬ngers,

or in extreme cases he may add his ten toes,

and lump the rest. Simplicity, simplicity, simplicity!

I say, let your aļ¬airs be as two or three,

and not a hundred or a thousand . . .

Simplify, simplify.

ā” HENRY DAVID THOREAU, Walden (1854)

The awesome memory of thy ever attentive computer

accepts all words as truth.

Think, therefore, in analytical, modular steps,

for the truth or untruth spoken through thy ļ¬ngertips

will be acted upon unerringly.

ā” HERMANN ZAPF, The Ten Commandments of Photo -Typesetting (1982)

(page 222)

27

Recovery

from

Errors

Chapter 27: Recovery from Errors 223

OK, everything you need to know about has been explainedā”unless error messages

badio.mf

you happen to be fallible. If you donā™t plan to make any errors, donā™t bother to !

read this chapter. Otherwise you might ļ¬nd it helpful to make use of some of to be read again

isolated expression

the ways that tries to pinpoint bugs in your programs. semicolon

In the trial runs you did when reading Chapter 5, you learned the general statement

form of error messages, and you also learned the various ways in which you can

respond to ā™s complaints. With practice, you will be able to correct

most errors āonline,ā as soon as has detected them, by inserting

and deleting a few things. On the other hand, some errors are more devastating

than others; one error might cause some other perfectly valid construction to be

loused up. Furthermore, doesnā™t always diagnose your errors cor-

rectly, since the number of ways to misunderstand the rules is vast;

is a rather simple-minded computer program that doesnā™t readily comprehend

the human point of view. In fact, there will be times when you and

disagree about something that you feel makes perfectly good sense. This chapter

tries to help avoid a breakdown in communication by explaining how to learn

ā™s reasons for its actions.

Ideally youā™ll be in a mellow mood when you approach , and

you will regard any error messages as amusing puzzlesā”āWhy did the machine

do that?āā”rather than as personal insults. knows how to issue more

than a hundred diļ¬erent sorts of error messages, and you probably never will

encounter all of them, because some types of mistakes are very hard to make.

Letā™s go back to the ā˜badio.mfā™ example ļ¬le of Chapter 5, since it has

more to teach us. If you have a better memory than the author, youā™ll recall

that the ļ¬rst error message was

>> mode.setup

! Isolated expression.

<to be read again>

;

l.1 mode setup;

% an intentional error!

?

In Chapter 5 we just charged ahead at this point, but it would be more normal

for a mature er to think āShucks, I meant to type ā˜mode_setupā™, but

I forgot the underscore. Luckily this didnā™t cause any harm; just

found an isolated expression, ā˜mode.setup ā™, which it will ignore. So let me now

insert the correct command, ā˜mode setupā™.ā

Good thinking; so you type ā˜I mode_setupā™, right? Wrong . . . sorry. Lots

of error messages occur before has read a semicolon in preparation

for another statement; the important clue in this case comes from the two lines

<to be read again>

;

224 Chapter 27: Recovery from Errors

which tell us that the semicolon is still pending. So the correct response would Extra tokens will be ļ¬‚ushed

ļ¬‚ushing

have been to type ā˜I; mode_setupā™ instead. Without the semicolon, you get semicolon

what appears at ļ¬rst to be a horrible mess: Undeļ¬ned coordinate

misspelling

! Extra tokens will be flushed. typographic errors

???

<to be read again> OK

warningcheck dependent variable

independent variable

mode_setup->warningcheck

:=0;if.unknown.mode:mode=proof;fi...

<insert> mode_setup

<to be read again>

;

l.1 mode setup;

% an intentional error!

?

But relax, thereā™s a simple way out. The help message says ā˜Please insert a

semicolon now in front of anything that you donā™t want me to deleteā™; all you

have to do is type ā˜I;ā™ and the net eļ¬ect will be the same as if you had correctly

inserted a semicolon before mode_setup in the ļ¬rst place.

The moral of this story is: When you insert a new statement during

error recovery, you frequently need to put a semicolon just ahead of it. But if

you forget, gives you another chance.

After proceeding through badio with the interactions suggested in Chap-

ter 5, we will come again to the error

>> 0.08682thinn+144

! Undefined x coordinate has been replaced by 0.

(This is where the erroneous ā˜thinnā™ was detected.) The help message for this

error has some curious advice:

(Chapter 27 of The METAFONTbook explains that

you might want to type ā˜I ???ā™ now.)

Chapter 27? Thatā™s us! What happens if we do type ā˜I ???ā™ now? We get

x4l=0.08682thinn+144

y4=-0.4924thinn+259.0005

x4r=-0.08682thinn+144

y4r=-0.9848thinn+259.0005

! OK.

It is now abundantly clear that ā˜thinā™ was misspelled. Plain deļ¬nes

ā˜???ā™ to be a macro that shows all of the current dependencies between numeric

variables and stops with ā˜OKā™; this is useful because a badly typed variable name

might have become a dependent variable instead of an independent variable, in

which case it would be revealed by ā˜???ā™ but not by the error message.

Chapter 27: Recovery from Errors 225

One more example of online error correction should suļ¬ce to make the

general strategy clear. Suppose you accidentally type square brackets instead of

parentheses; the computer will scream:

! A primary expression canā™t begin with ā˜[ā™.

<inserted text>

0

<to be read again>

[

<*> show round[

1 + sqrt43];

?

(By coincidence, the help message for this particular error also refers to Chap-

ter 27.) When needs to see an expression, because of the tokens it

has already digested, it will try to insert ā˜0ā™ in order to keep going. In this case

we can see that zero isnā™t what we intended; so we type ā˜7ā™ to delete the next

seven tokens, and the computer comes back with

<*> show round[1 + sqrt43]

;

?

Now ā˜I (1 + sqrt43)ā™ will insert the correct formula, and the program will be

able to continue happily as if there were no mistake.

EXERCISE 27.1

Why was ā˜7ā™ the right number of tokens to delete?

EXERCISE 27.2

If the user hadnā™t deleted or inserted anything, but had just plunged ahead,

would have come up with another error:

>> 0

! Extra tokens will be flushed.

<to be read again>

[

<to be read again>

(7.55743)

<to be read again>

]

<*> show round[1 + sqrt43]

;

?

Explain what happened. What should be done next?

Itā™s wise to remember that the ļ¬rst error in your program may well

spawn spurious āerrorsā later on, because anomalous commands can inļ¬‚ict se-

rious injury on ā™s ability to cope with the subsequent material. But

226 Chapter 27: Recovery from Errors

most of the time you will ļ¬nd that a single run through the machine will locate batchmode

nonstopmode

all of the places in which your input conļ¬‚icts with ā™s rules. emergency stop

end

Sometimes an error is so bad that is forced to quit prematurely. Fatal base ļ¬le error

For example, if you are running in batchmode or nonstopmode, - loop

inļ¬nite loop

makes an āemergency stopā if it needs input from the terminal; this happens

I canā™t go on

when a necessary ļ¬le canā™t be opened, or when no end was found in the input. Here This canā™t happen

are some of the messages you might get just before gives up the ghost: METAFONT capacity exceeded

Fatal base file error; Iā™m stymied.

This means that the preloaded base you have speciļ¬ed cannot be used, because it was

prepared for a diļ¬erent version of .

That makes 100 errors; please try again.

has scrolled past 100 errors since the last statement ended, so itā™s probably

in an endless loop.

I canā™t go on meeting you like this.

A previous error has gotten out of whack. Fix it and try again.

This canā™t happen.

Something is wrong with the you are using. Complain ļ¬ercely.

Thereā™s also a dreadful message that issues only with great reluc-

tance. But it can happen:

METAFONT capacity exceeded, sorry.

This, alas, means that you have tried to stretch too far. The message

will tell you what part of ā™s memory has become overloaded; one of the

following eighteen things will be mentioned:

(strings and names of symbolic tokens and ļ¬les)

number of strings

(the characters in such strings)

pool size

(pairs, paths, pens, pictures, token lists, transforms, etc.)

main memory size

(symbolic token names)

hash size

(simultaneous input sources)

input stack size

(internal quantities)

number of internals

(transitions between octants in cycles)

rounding table size

(macro parameters)

parameter stack size

(characters in lines being read from ļ¬les)

buffer size

(input ļ¬les and error insertions)

text input levels

(key points per path)

path size

(rows of picture being simultaneously accessed)

move table size

(pen oļ¬sets per octant)

pen polygon size

(accumulated ligtable instructions)

ligtable size

(distinct kern amounts)

kern

(built-up characters)

extensible

(largest headerbyte address)

headerbyte

(largest fontdimen address)

fontdimen

The current amount of memory available will also be shown.

Chapter 27: Recovery from Errors 227

If you have a job that doesnā™t overļ¬‚ow ā™s capacity, yet you want tracingstats

stack positions

to see just how closely you have approached the limits, just set tracingstats

memory usage

to a positive value before the end of your job. The log ļ¬le will then conclude with a recursion

report on your actual usage of the ļ¬rst nine things named above (i.e., the number of interrupt

inļ¬nite loops

strings, . . . , the buļ¬er size), in that order. Furthermore, the showstats command can errorstopmode

be used to discover the current string memory and main memory usage at any time show

hide

during a run. The main memory statistics are broken into two parts; ā˜490&5950ā™ means,

for example, that 490 words are being used for ālargeā things like pens, capsules, and

transforms, while 5950 words are being used for āsmallā things like tokens and edges.

What can be done if ā™s capacity is exceeded? All of the above-listed

components of the capacity can be increased, except the memory for kerns and

extensible characters, provided that your computer is large enough; in fact, the space

necessary to increase one component can usually be obtained by decreasing some other

component, without increasing the total size of . If you have an especially

important application, you may be able to convince your local system people to provide

you with a special whose capacities have been hand-tailored to your needs.

But before taking such a drastic step, be sure that you are using properly.

If you have speciļ¬ed a gigantic picture that has lots of transitions between black and

white pixels, you should change your approach, because has to remember

every change between adjacent pixel values in every currently accessible picture. If you

keep saving diļ¬erent pens, you might be wasting memory as discussed in Chapter 16. If

you have built up an enormous macro library, you should realize that has

to remember all of the replacement texts that you deļ¬ne; therefore if memory space is

in short supply, you should load only the macros that you need.

Some erroneous programs will overļ¬‚ow any ļ¬nite memory capac-

ity. For example, after ā˜def recurse=(recurse)enddefā™, the use of recurse

will immediately bomb out:

! METAFONT capacity exceeded, sorry [input stack size=30].

recurse->(recurse

)

recurse->(recurse

)

recurse->(recurse

)

...

The same sort of error will obviously occur no matter how much you increase -

ā™s input stack size.

Most implementations of allow you to interrupt the program in

some way. This makes it possible to diagnose the causes of inļ¬nite loops,

if the machine doesnā™t stop because of memory limitations. switches to

errorstopmode when interrupted; hence you have a chance to insert commands into

the input: You can abort the run, or you can show or change the current contents

of variables, etc. In such cases you will probably want to āhideā your diagnostic com-

mands, for example by typing

I hide(showstopping:=1; alpha:=2; show x)

228 Chapter 27: Recovery from Errors

so that you donā™t mess up the expression is currently evaluating. Inter- strange

El Palo Alto

ruption can also give you a feeling for where is spending most of its time,

ļ¬‚ex

if you happen to be using an ineļ¬cient macro, since random interrupts will tend to for

occur in whatever place visits most often. octant

SSW

compass directions

ā™s second most frustrating error messages are its occasional claims

SSE

that you have āstrangeā paths. Sometimes a glance at your output will make ESE

it clear that you did indeed specify a path that crossed over itself, something like a WSW

WNW

ļ¬gure-8; but sometimes a path that looks ļ¬ne to you will be rejected by the computer. NNE

In such cases you need to decipher ā™s octant codes, which look scary at ļ¬rst NNW

ENE

although they turn out to be helpful when you get used to them. For example, letā™s

reconsider branch4 of El Palo Alto, from the program in Chapter 14:

branch4=

flex((0,509),(-14,492),(-32,481))

&flex((-32,481),(-42,455),(-62,430))

&flex((-62,430),(-20,450),(42,448))

&flex((42,448),(38,465),(4,493),(0,509))

&cycle;

If the number 450 in the third ļ¬‚ex had been 452 instead, would have

stopped and told you this:

> 0 SSW WSW 1 2 SSW 3 WSW 4 (WNW NNW) NNE ENE 5 ESE 6 (ENE)

NNE NNW 7 WNW NNW 8 NNE 0 (NNW WNW WSW)

! Strange path (turning number is zero).

<to be read again>

;

<for(4)> ...]shifted(150,50)scaled(w/300);

ENDFOR

p.4,l.94 endfor

endchar;

?

The ā˜for(4)ā™ in the ļ¬fth-last line implies that branch4 is at fault, because it says that

the for loop index is 4; but the octant codes like ā˜SSWā™ are your only clues about why

branch4 is considered strange. (A simpler example appeared in Chapter 13, which

you might want to review now.) You probably also have a proofmode diagram:

(Figure 27a will be inserted here; too bad you canā™t see it now.)

Starting at time 0, and at the point (0, 509), the path goes South by Southwest, then

West by Southwest until time 2 (the end of the ļ¬rst ļ¬‚ex). Then it goes SSW again,

Chapter 27: Recovery from Errors 229

and WSW again (thatā™s the second ļ¬‚ex). But at time 4, the path makes a sharp turn counterclockwise

eps

through the directions WNW and NNW, without moving (because these octant codes are

turningcheck

in parentheses). Aha! Thatā™s where the path was supposed to turn counterclockwise, tracingspecs

through SSW and SSE and ESE; turned clockwise because it was the shortest tracingtitles

tracingmacros

way to go. The path actually makes a little loop at time 4, between the end of the tracingrestores

second ļ¬‚ex and the beginning of the third. Therefore its turning number is indeed zero, tracingequations

tracingchoices

and the path is strange by deļ¬nition.

control points

tracingpens

EXERCISE 27.3

tracingoutput

At what point do the second and third ļ¬‚exes cross, in this example?

There are three main ways to avoid problems with strange paths. One is to

stay away from paths that turn so abruptly. Or you can displace the paths by

epsilon , as in the serif example at the end of Chapter 16. (Displacing by eps would be

even safer.) Or you can discipline yourself to ļ¬ll all cycles counterclockwise, so that

you can set turningcheck := 0; this means that wonā™t check for strange

paths, but thatā™s OK because tiny little loops wonā™t hurt anything if you are ļ¬lling

cycles in the correct direction.

Sometimes the octant codes of a strange path are shown backwards, because

the system may have tried to reverse the path to get rid of its strangeness.

Sooner or laterā”hopefully soonerā”youā™ll get to process

your whole ļ¬le without stopping once to complain. But maybe the output still

wonā™t be right; the mere fact that didnā™t stop doesnā™t mean that

you can avoid looking at proofsheets. At this stage itā™s usually easy to see how

to ļ¬x typographic errors by correcting the input; hardcopy proofs such as those

discussed in Appendix H usually clear up obvious mistakes, especially if you have

remembered to label the key points in your constructions.

But your output may contain seemingly inexplicable errors. If you canā™t

ļ¬nd out what went wrong, try the old trick of simplifying your program: Remove

all the things that do work, until you obtain the shortest possible input ļ¬le that

fails in the same way as the original. The shorter the ļ¬le, the easier it will be

for you or somebody else to pinpoint the problem.

One of the important tricks for shortening a buggy program is to assign a

positive value to tracingspecs , because this will put all the key points and

control points of a problematic path into your log ļ¬le. (See the example at the end

of Chapter 24, ābefore subdivision.ā) If something is wrong with the treatment of

some path, you can copy the pathā™s description from the log ļ¬le and use it directly in

input, thereby avoiding all the complexity of equations that might have

been involved in that pathā™s original creation.

Weā™ve just talked about tracingstats and tracingspecs ; is able

to produce lots of other kinds of tracing. For example, Chapter 22 discusses

tracingtitles , Chapter 18 discusses tracingmacros , Chapter 17 discusses tracingrestores ,

and Chapter 9 discusses tracingequations . You can also invoke tracingchoices , which

shows all paths before and after their control points are chosen according to the rules in

Chapter 14; or tracingpens , which shows the pen polygons that arise when a future pen

becomes a full-ļ¬‚edged pen; or tracingoutput , which shows every picture thatā™s shipped

230 Chapter 27: Recovery from Errors

out, using edge-transitions to represent the pixel values as illustrated in Chapter 13. interim

tracingcommands

Each of these types of tracing is enabled by assigning a positive value to the correspond-

command

ing internal quantity; for example, you can simply set tracingpens := 1 (or interim expandable token

tracingpens := 1) if you want the data about pens. algebraic

operation

Digitized output

If tracingcommands = 1, shows every command just before it is tracingedges

carried out. If tracingcommands = 2, also shows every expand- lowres

mag

able token just before it is expanded (except that macros are separate, theyā™re traced

draw

only when tracingmacros > 0). And if tracingcommands = 3, also shows ļ¬lldraw

every algebraic operation just before it is evaluated. Thus you can get āstream of octant

transition

consciousnessā information about everything is doing.

penoļ¬set

tracingonline

Digitized output can be monitored by setting tracingedges = 1. For example, tracingall

if we ask to draw the Ionian ā˜ ā™ of Chapter 5 at a resolution of showstopping

show

100 pixels per inch (lowres mode with mag = .5), tracingedges will report as follows:

loggingall

interact

Tracing edges at line 15: (weight 1) tracingnone

tracingstats

(1,5)(1,2)(2,2)(2,1)(3,1)(3,0)(8,0)(8,1)(9,1)(9,2)(10,2)(10,8)(9,8)

(9,9)(8,9)(8,10)(3,10)(3,9)(2,9)(2,8)(1,8)(1,5).

Tracing edges at line 15: (weight -1)

(3,5)(3,2)(4,2)(4,1)(7,1)(7,2)(8,2)(8,8)(7,8)(7,9)(4,9)(4,8)(3,8)(3,5).

By following these edges (and negating their weights on the inner boundary) we ļ¬nd

that the character at this low resolution is symmetric:

Further information about digitization comes out when tracingedges > 1, if

ļ¬xed pens are used to draw or ļ¬lldraw a shape. In this case detailed informa-

tion is presented about the activity in each octant direction; straight line ātransitionā

edges are also reported whenever changes from one penoļ¬set to another.

The tracing . . . commands put all of their output into your log ļ¬le, unless the

tracingonline parameter is positive; in the latter case, all diagnostic informa-

tion goes to the terminal as well as to the log ļ¬le. Plain has a tracingall

macro that turns on the maximum amount of tracing of all kinds. It not only sets up

tracingcommands , tracingedges , tracingspecs , and so on, it also sets tracingonline := 1,

and it sets showstopping := 1 so that you can do interactive debugging via show com-

mands. This is the works. Thereā™s also loggingall, which is like tracingall except

that it doesnā™t touch tracingonline or showstopping . You can say interact if you want

just tracingonline := showstopping := 1. Finally, thereā™s tracingnone, which shuts oļ¬

every form of tracing after youā™ve had enough.

Some production versions of have been streamlined for speed.

These implementations donā™t look at the value of tracingstats , nor do you

get extra information when tracingedges > 1, because runs faster when it

doesnā™t have to maintain statistics or keep tabs on whether tracing is required. If you

want all of ā™s diagnostic tools, you should be sure to use the right version.

Chapter 27: Recovery from Errors 231

If you set pausing := 1, will give you a chance to edit each line of pausing

lies

input as it is read from the ļ¬le. In this way you can make temporary patches

jokes

(e.g., you can insert show. . . commands) while troubleshooting, without changing the FONT

actual contents of the ļ¬le, and you can keep running at human speed. HEIN

Final hint: When working on a large font, itā™s best to prepare only a

few characters at a time. Set up a ātestā ļ¬le and a āmasterā ļ¬le, and do your

work in the test ļ¬le. (Appendix E suggests a convenient way to prepare control

ļ¬les that supply parameters to individual test characters as well as to the whole

font.) After the characters come out looking right, you can append them to the

master ļ¬le; and you can run the master ļ¬le through occasionally, in

order to see how the font is shaping up. Characters can always be moved back

to the test ļ¬le if you have to ļ¬x some unexpected problems.

EXERCISE 27.4

Final exercise: Find all of the lies in this manual, and all of the jokes.

Final exhortation: GO FORTH now and create masterpieces of digital typography!

With respect to the directions of the route

I may have made some errors.

ā” FRAY PEDRO FONT, Diary (1776)

The road to wisdom? Well, itā™s plain

and simple to express:

Err

and err

and err again

but less

and less

and less.

ā” PIET HEIN, Grooks (1966)

(page 232)

A

Answers to

All the

Exercises

Appendix A: Answers to All the Exercises 233

The preface to this manual points out the wisdom of trying to ļ¬gure out each whatever

dir

exercise before you look up the answer here. But these answers are intended to

be read, since they occasionally provide additional information that you are best

equipped to understand when you have just worked on a problem.

2.1. Point 5 = (100, 0) is closer than any of the others. (See the diagram below.)

2.2. False. But they all do have the

same y coordinate.

(Figure A2a will be inserted here; too bad you

2.3. 5 units to the left of the reference canā™t see it now.)

point, and 15 units up.

2.4. (200, ā’60).

2.5. top lft z1 = (0, b); top z2 = (a, b); top rt z3 = (2a ā’ 1, b); bot lft z4 = (0, 0);

bot z5 = (a, 0); bot rt z6 = (2a ā’ 1, 0). Adjacent characters will be separated by exactly

one column of white pixels, if the character is 2a pixels wide, because the right edge of

black pixels is speciļ¬ed here to have the x coordinate 2a ā’ 1.

2.6. right = (1, 0); left = (ā’1, 0); down = (0, ā’1); up = (0, 1).

2.7. True; this is (2, 3) ā’ (5, ā’2).

2.8. 0[z1 , z2 ] = z1 , because we move none of the way towards z2 ; similarly 1[z1 , z2 ]

simpliļ¬es to z2 , because we move all of the way. If we keep going in the same direction

until weā™ve gone twice as far as the distance from z1 to z2 , we get to 2[z1 , z2 ]. But if

we start at point z1 and face z2 , then back up exactly half the distance between them,

we wind up at (ā’.5)[z1 , z2 ].

2.9. (a) True; both are equal to z1 + 1 (z2 ā’ z1 ). (b) False, but close; the right-hand

2

side should be 2 z1 + 1 z2 . (c) True; both are equal to (1 ā’ t)z1 + tz2 .

3 3

2.10. There are several reasons. (1) The equations in a program should

represent the programmerā™s intentions as directly as possible; itā™s hard to understand

those intentions if you are shown only their ultimate consequences, since itā™s not easy

to reconstruct algebraic manipulations that have gone on behind the scenes. (2) Itā™s

easier and safer to let the computer do algebraic calculations, rather than to do them

by hand. (3) If the speciļ¬cations for z1 and z5 change, the formula ( 1 [x1 , x5 ], b) still

2

gives a reasonable value for z3 . Itā™s almost always good to anticipate the need for

subsequent modiļ¬cations.

However, the stated formula for z3 isnā™t the only reasonable way to proceed.

We could, for example, give two equations

x3 ā’ x1 = x5 ā’ x3 ; y3 = b;

the ļ¬rst of these states that the horizontal distance from 1 to 3 is the same as the

horizontal distance from 3 to 5. Weā™ll see later that is able to solve a wide

variety of equations.

2.11. The following four equations suļ¬ce to deļ¬ne the four unknown quantities x2 ,

y2 , x4 , and y4 : z4 ā’ z2 = whatever ā— dir 20; 1 [y2 , y4 ] = 2 [y3 , y1 ]; z2 = whatever [z1 , z3 ];

2 3

z4 = whatever [z3 , z5 ].

234 Appendix A: Answers to All the Exercises

3.1. The direction at z2 is parallel to the line z4 . . z3 , but the vector z4 ā’ z3 drawdot

speciļ¬es a direction towards z4 , which is 180ā—¦ diļ¬erent from the direction z3 ā’ z4 that one point

ā“

was discussed in the text. Thus, we have a diļ¬cult speciļ¬cation to meet, and - curl

draws a pretzel-shaped curve that loops around in a way thatā™s too ugly to

show here. The ļ¬rst part of the path, from z4 to z2 , is mirror symmetric about the

line z1 . . z5 that bisects z4 . . z2 , so it starts out in a south-by-southwesterly direction;

the second part is mirror symmetric about the vertical line that bisects z2 . . z3 , so

when the curve ends at z3 itā™s traveling roughly northwest. The moral is: Donā™t specify

a direction that runs opposite to (i.e., is the negative of) the one you really want.

3.2. draw z5 . . z4 {z4 ā’ z2 } . . z1 . . z3 . . z6 {z2 ā’ z6 } . . cycle.

4.1. (a) An ellipse 0.8 pt tall and 0.2 pt wide (ā˜ ā™); (b) a circle of diameter 0.8 pt

(rotation doesnā™t change a circle!); (c) same as (a).

4.2. Six individual points will be drawn, instead of lines or curves. These points will

be drawn with the current pen. However, for technical reasons explained in Chapter 24,

the draw command does its best work when it is moving the pen; the pixels you get

at the endpoints of curves are not always what you would expect, especially at low

resolutions. It is usually best to say ā˜drawdotā™ instead of ā˜drawā™ when you are drawing

only one point.

4.3. True, for all of the pens discussed so far. But false in general, since we will see

later that pens might extend further upward than downward; i.e., t might be unequal

to b in the equations for top and bot .

4.4. x2 = x1 ; x3 = 1 [x2 , x4 ]; x4 = x5 ; bot y1 = ā’o; top y2 = h + o; y4 = y2 ;

2

y5 = y1 ; draw z1 . . z2 ; draw z2 . . z3 ; draw z3 . . z4 ; draw z4 . . z5 . We will learn

later that the four draw commands can be replaced by

draw z1 - - z2 - - z3 - - z4 - - z5 ;

in fact, this will make run slightly faster.

4.5. Either say ā˜ļ¬ll z5 . . z4 . . z1 . . z3 . . z6 . . z5 . . cycleā™, which doubles point z5

and abandons smoothness there, or ā˜ļ¬ll z5 {curl 1} . . z4 . . z1 . . z3 . . z6 . . {curl 1}cycleā™.

In the latter case you can omit either one of the curl speciļ¬cations, but not both.

4.6. After the six original points have been deļ¬ned, say

ļ¬ll z5 . . z4 . . z1 . . z3 . . z6 . . cycle;

z0 = (.8[x1 , x2 ], .5[y1 , y4 ]);

for k = 1 upto 6: zk = .2[zk , z0 ]; endfor

unļ¬ll z5 . . z4 . . z1 . . z3 . . z6 . . cycle.

1

[North , 1 [North , West ]] = 1 [90, 1 [90, 180]] = 1 [90, 135] = 112.5.

4.7. 2 2 2 2 2

4.8. 30ā—¦ , 60ā—¦ , 210ā—¦ , and 240ā—¦ . Since itā™s possible to add or subtract 360ā—¦ without

changing the meaning, the answers ā’330ā—¦ , ā’300ā—¦ , ā’150ā—¦ , and ā’120ā—¦ are also correct.

4.9. z1l = (25, 30), z1r = (25, 20).

4.10. He said ā˜penstroke z1e {up } . . z2e {left } . . z3e {down } . . z4e {right } . . cycleā™.

Appendix A: Answers to All the Exercises 235

4.11. We use angles perpendicular to (w, h) and (w, ā’h) at the diagonal endpoints: perpendicular

S

x1l = x4l = 0; direction

trial path

x2 = x5 = .5w; perpendicular

x3r = x6r = w;

y1r = y2 = y3l = h;

y4r = y5 = y6l = 0;

z1 = .25[z1 , z6 ]; z6 = .75[z1 , z6 ];

theta1 := angle(w, ā’h) + 90;

penpos1 (b, theta1 ); penpos6 (b, theta1 );

z7 = .5[z1 , z6 ]; penpos7 (.6b, theta1 );

penpos1 (b, theta1 ); penpos6 (b, theta1 );

penstroke z1e . . z1 e {z6 ā’ z1 } . . z7e . . {z6 ā’ z1 }z6 . . z6e ;

e

z3 = .25[z3 , z4 ]; z4 = .75[z3 , z4 ];

theta3 := angle(ā’w, ā’h) + 90;

penpos3 (b, theta3 ); penpos4 (b, theta3 );

z8 = .5[z1 , z6 ]; penpos8 (.6b, theta3 );

penpos3 (b, theta3 ); penpos4 (b, theta3 );

penstroke z3e . . z3 e {z4 ā’ z3 } . . z8e . . {z4 ā’ z3 }z4 . . z4e ;

e

penpos2 (b, 0); penpos5 (b, 0); penstroke z2e . . z5e .

5.1. The width is 0.8em#, and an em# is 10 true points, so the box will be exactly

8 pt wide in device-independent units. The height will be 7 pt. (And the depth below

the baseline will be 0 pt.)

5.2. 8 Ć— 3.6 = 28.8 rounds to the value w = 29; similarly, h = 25. (And d = 0.)

5.3. Hereā™s one way, using a variable slab to control the pen breadth at the ends

of the stroke:

slab#:=.8pt#; define_blacker_pixels(slab);

beginchar("S",5/9em#,cap#,0); "The letter S";

penpos1(slab,70); penpos2(.5slab,80);

penpos3(.5[slab,thick],200); penpos5(.5[slab,thick],210);

penpos6(.7slab,80);

penpos7(.25[slab,thick],72);

x1=x5; y1r=.94h+o;

x2=x4=x6=.5w; y2r=h+o; y4=.54h; y6l=-o;

x3r=.04em; y3=.5[y4,y2];

x5l=w-.03em; y5=.5[y4,y6];

(Figure A5a will be inserted

.5[x7l,x7]=.04em; y7l=.12h-o; here; too bad you canā™t see it

path trial; trial=z3{down}..z4..{down}z5; now.)

pair dz; dz=direction 1 of trial;

penpos4(thick,angle dz-90);

penstroke z1e..z2e{left}..z3e{down}

..z4e{dz}..z5e{down}..z6e{left}..z7e;

penlabels(1,2,3,4,5,6,7); endchar;

Notice that the pen angle at point 4 has been found by letting construct

a trial path through the center points, then using the perpendicular direction. The

letters work reasonably well at their true size: ā˜ .ā™

236 Appendix A: Answers to All the Exercises

5.4. After an āisolated expression,ā thinks it is at the end of a state- zero

enormous number

ment or command, so it expects to see a semicolon next. You should type, e.g.,

backslash

ā˜I; mode_setupā™ to keep happy. begingroup

save

5.5. Yes. endgroup

1

6.1. (a) No, the second token represents 65536 . (A token has the same meaning

as ā˜0ā™ if and only if its decimal value is strictly less than 2ā’17 = .00000 76293 94531 25.)

1

(b) Yes; both tokens represent 65536 , because 1 is the nearest integer to both .00001 Ć—

2

65536 = .65536 and 0.00002 Ć— 65536 = 1.31072. (c) No, 0.00003 represents 65536 .

(d) Yes, they both mean āenormous number that needs to be reducedā;

complains in both cases and substitutes the largest legal numeric token. (Rounding

1

4095.999999 to the nearest multiple of 65536 yields 4096, which is too big.)

6.2. xx , 3.1 (a numeric token), .6 (another numeric token), .. , [[ , a ,

+- , bc d , e , ] , ] , "a %" (a string token), <|> , ( (see rule 5), ( , $ , 1 (a

numeric token), 5 (likewise numeric), "+-" (a string token), and "" (a string token

that denotes an empty sequence of characters). All of these tokens are symbolic unless

otherwise mentioned. (Notice that four of the spaces and two of the periods were

deleted by rule 1. One way to verify that ļ¬nds precisely these tokens is to

prepare a test ļ¬le that says ā˜isolated expression;ā™ on its ļ¬rst line and that contains

the stated text on its second line. Then respond to ā™s error message by

repeatedly typing ā˜1ā™, so that one token is deleted at a time.)

6.3. The statement is basically true but potentially misleading. You can insert any

number of spaces between tokens without changing the meaning of a program, but you

cannot insert a space in the middle of any token without changing something. You can

delete spaces between tokens unless that would āglueā two adjacent tokens together.

6.4. False. It may seem that this new sort of numeric token would be recognized

only in cases where the period is not followed by a digit, hence the period would be

dropped anyway by rule 1. However, the new rule would have disastrous consequences

in a line like ā˜draw z1..z2ā™ !

7.1. You can put a space between the subscripts, as in ā˜a1 5ā™. (Weā™ll see later that

a backslash acts as a null symbol, hence ā˜a1\5ā™ is another solution.)

7.2. No; a[-1] canā™t be accessed without using [ and ]. The only other form of

subscript is numeric token , which canā™t be negative. (Well, strictly speaking, you

could say ā˜let ?=[; let ??=]ā™ and then refer to ā˜a?-1??ā™; but thatā™s cheating.)

7.3. Assuming that ā˜+ā™ was still a spark when he said ā˜let plus=+ā™, he canā™t refer

to the variable ā˜a.plus1ā™ unless he changes the meaning of plus again to make it a tag.

(We will eventually learn a way to do this without permanently clobbering plus, as

follows: ā˜begingroup save plus; a.plus1 endgroupā™.)

7.4. True. (But a suļ¬x is not always a variable .)

7.5. Yes, because it removes any existing value that x may have had, of whatever

type; otherwise you couldnā™t safely use x in a numeric equation. Itā™s wise to declare

numeric variables when youā™re not sure about their former status, and when youā™re

sure that you donā™t care what their previous value was. A numeric declaration together

with a comment also provides useful documentation. (Incidentally, ā˜numeric xā™ doesnā™t

aļ¬ect other variables like ā˜x2ā™ or ā˜x.xā™ that might be present.)

Appendix A: Answers to All the Exercises 237

7.6. (a) The ā˜42ā™ is illegal because subscripts must be collective. (b) The ā˜24ā™ is **

FORTRAN

illegal because a declared variable must start with a symbolic token , not a numeric

or

token. (c) Thereā™s nothing wrong with the consecutive commas; the second comma Ā”

begins a declared variable , so it loses its former meaning and becomes a tag. Thus Āæ

accuracy

tries to declare the variable ā˜,t,pathā™. However, ā˜pathā™ cannot appear in

a suļ¬x, since itā™s a spark. (Yes, this is admittedly tricky. Computers follow rules.)

8.1. ((z1+z2)..((z3/4)*5))..(z6-(7*(8z9))).

8.2. The fraction 100/3 is evaluated ļ¬rst (because such divisions take precedence);

the rounding error in this fraction is then magniļ¬ed by 100.

8.3. A sqrt takes precedence over any operation with two operands, hence the

machine computes ā˜(sqrt 2)**2ā™; was somewhat lucky that the answer

turned out to be exactly 2. (The sqrt operation computes the nearest multiple of

1

, and the rounding error in this quantity is magniļ¬ed when it is squared. If you try

65536

sqrt 3**2, youā™ll get 3.00002; also sqrt 2**4 turns out to be 4.00002.) Incidentally,

the ** operation of plain has the same precedence as * and /; hence

ā˜x*y**2ā™ means the same as ā˜(x*y)**2ā™, and ā˜-x**2ā™ means ā˜(-x)**2ā™, contrary to the

conventions of FORTRAN.

8.4. Since ā˜orā™ has stronger precedence than ā˜<ā™ or ā˜>ā™, tries to eval-

uate this expression by putting things in parentheses as follows: ā˜(0 > (1 or a)) < aā™.

Now ā˜1 or aā™ makes no sense, because ā˜orā™ operates only on booleans; in such cases

uses the right operand ā˜aā™ as the result. Then ā˜0 > aā™ is indeterminate

because a is unknown; treats this as false. Finally ā˜false < aā™ is another

illegal combination of types.

8.5. The token ā˜++-ā™ is undeļ¬ned, so it is a tag; therefore ++-7 is a subscripted

variable, which was multiplied by zero.

8.6. The associative law is valid for exact computations, but not for rounded com-

putations. For example, it fails even in the case of multiplication, since (.1 ā— .1) ā— 10 =

0.09995 while .1 ā— (.1 ā— 10) = .1 when products are rounded to the nearest multiples

1

of 65536 . However, this observation doesnā™t quite explain the stated example, which

would have yielded 7 in all cases if ā had computed 2 ++ 4 with full accu-

racy! The closest approximation to 20 is 4 65536 , but 2 ++ 4 turns out to be 4 30941

30942

65536

instead. computes the absolutely best possible approximations to the true

answers when it does multiplications, divisions, and square roots, but not when it does

Pythagorean operations.

8.7. Itā™s impossible to make an expression from ā˜ numeric token numeric token ā™,

because the rule for scalar multiplication operator speciļ¬cally prohibits this. -

will recognize the ļ¬rst ā˜2ā™ as a numeric primary , which is ultimately regarded

as a numeric expression ; the other ā˜2ā™ will probably be an extra token that is ļ¬‚ushed

away after an error message has been given.

8.8. If a numeric token is followed by ā˜/ numeric token ā™ but not preceded by

ā˜ numeric token /ā™, the syntax allows it to become part of an expression only by us-

ing the ļ¬rst case of numeric token primary . Therefore ā˜1/2/3/4ā™ must be treated as

ā˜(1/2)/(3/4)ā™, and ā˜a/2/3/4ā™ must be treated as ā˜a/(2/3)/4ā™.

238 Appendix A: Answers to All the Exercises

8.9. string primary ā’ā’ string variable rotated

| string token dotprod

abs

| ( string expression ) ypart

| substring pair expression of string primary pythagorean subtraction

unitvector

string secondary ā’ā’ string primary length

string tertiary ā’ā’ string secondary

string expression ā’ā’ string tertiary

| string expression & string tertiary

(The full syntax in Chapter 25 includes several more varieties of string primary that

havenā™t been hinted at yet.)

9.1. (a) Point 1 should lie nine pixels to the left of point 7, considering horizontal

positions only; no information is given about the vertical positions y1 or y7 . (b) Point 7

should sit directly above or below point 4, and its distance up from the baseline should

be halfway between that of points 4 and 5. (c) The left edge of the currently-picked-up

pen, when that pen is centered at point 21, should be one pixel to the right of its right

edge when at point 20. (Thus there should be one clear pixel of white space between

the images of the pen at points 20 and 21.)

9.2. (a) y13 = ā’y11 (or ā’y13 = y11 , or y13 + y11 = 0). (b) z10 = z12 + (mm , ā’1).

(c) z43 = 1 [(0, h), (w, ā’d)].

3

9.3. (a) z1 = z2 = z3 = (w, h); z4 = z5 = z6 = (0, 0). (b) z1 = z6 = (.5w, .5h);

z2 = (.75w, .75h); z3 = (w, h); z4 = (0, 0); z5 = (.25w, .25h).

9.4. z = whatever [z1 , z2 ]; z = whatever [z3 , z4 ]. (Incidentally, itā™s interesting

to watch this computation in action. Run with \tracingequations:=

tracingonline:=1 and say, for example,

z=whatever[(1,5),(8,19)]; z=whatever[(0,17),(6,1)];

the solution appears as if by magic. If you use alpha and beta in place of the whatevers,

the machine will also calculate values for alpha and beta .)

9.5. z = whatever [z1 , z2 ]; z ā’ z3 = whatever ā— (z5 ā’ z4 ).

9.6. z11 ā’ z12 = whatever ā— (z13 ā’ z14 ) rotated 90, assuming that z13 ā’ z14 is known.

(Itā™s also possible to say ā˜(z11 ā’z12 )dotprod(z13 ā’z14 ) = 0ā™, although this risks overļ¬‚ow

if the coordinates are large.)

9.7. One solution constructs the point z4 on z2 . . z3 such that z4 . . z1 is per-

pendicular to z2 . . z3 , using ideas like those in the previous two exercises: ā˜z4 =

whatever [z2 , z3 ]; z4 ā’ z1 = whatever ā— (z3 ā’ z2 ) rotated 90ā™. Then the requested dis-

tance is length(z4 ā’ z1 ). But thereā™s a slicker solution: Just calculate

abs ypart((z1 ā’ z2 ) rotated ā’angle(z3 ā’ z2 )).

9.8. It would be nice to say simply ā˜z = whatever [z2 , z3 ]ā™ and then to be able to

say either ā˜length(z ā’ z1 ) = lā™ or ā˜z ā’ z1 = (l, 0) rotated whatever ā™; but neither of the

second equations is legal. (Indeed, there couldnā™t possibly be a legal solution that has

this general ļ¬‚avor, because any such solution would determine a unique z, while there

are two points to be determined.) The best way seems to be to compute z4 as in the

previous exercise, and then to let v = (l +ā’+ length(z4 ā’ z1 )) ā— unitvector(z3 ā’ z2 ); the

desired points are then z4 + v and z4 ā’ v.

Appendix A: Answers to All the Exercises 239

9.9. Such an equation tells us nothing new about a or b. Indeed, each use of whatever

CAPSULE

whatever introduces a new independent variable, and each new independent variable

capsule

āuses upā one equation, since we need n equations to determine the values of n un- hash hash

knowns. On the other hand an equation between pairs counts as two equations; so tracingcapsules

thereā™s a net gain of one, when whatever appears in an equation between pairs.

10.1. Yes, but it must be done in two steps: ā˜numeric newcode ; newcode = code +1;

numeric code ; code = newcode ā™.

10.2. The assignment ā˜x3 := whatever ā™ does exactly what you want.

10.3. The result shows that s1 = s3 = s4 and s2 = s5 = s6 now:

s[]=unknown string

s1=unknown string s3

s2=unknown string s6

s3=unknown string s4

s4=unknown string s1

s5=unknown string s2

s6=unknown string s5

(The assignment s2 := s5 broke s2 ā™s former relationship with s1 , s3 , and s4 .)

10.4. The results are

## a=1

(after the ļ¬rst assignment)

## a=b+1

(after the second assignment)

## b=0.5a-0.5

(after the third assignment)

### -1.5a=-%CAPSULEnnnn-0.5

(after the third, see below)

## a=%CAPSULEnnnn

(after ā˜showā™; variable a is independent)

>> a

(this is the ļ¬nal value of b)

>> 0.33333a-0.33333

Let ak denote the value of a after k assignments were made. Thus, a0 = 1, and a1

was dependent on the independent variable b. Then a1 was discarded and b became

dependent on the independent variable a2 . The right-hand side of the third assignment

was therefore a2 + b. At the time a2 was about to be discarded, had two

dependencies b = 0.5a2 ā’ 0.5 and Īŗ = 1.5a2 ā’ 0.5, where Īŗ was a nameless ācapsuleā

inside of the computer, representing the new value to be assigned. Since Īŗ had a

higher coeļ¬cient of dependency than b, chose to make Īŗ an independent

variable, after which ā’1.5a2 was replaced by ā’Īŗ ā’ 0.5 in all dependencies; hence b was

equal to 0.33333Īŗ ā’ 0.33333. After the third assignment was ļ¬nished, Īŗ disappeared

and a3 became independent in its place. (The line ā˜## a=%CAPSULEnnnnā™ means that

a was temporarily dependent on Īŗ, before Īŗ was discarded. If the equation a = Īŗ had

happened to make Īŗ dependent on a, rather than vice versa, no ā˜##ā™ line would have

been printed; such lines are omitted when a capsule or part of a capsule has been made

dependent, unless you have made tracingcapsules > 0.)

11.1. Almost, but not quite. The values of standard dimension variables like pt

and mm will be identical in both setups, as will the values of ad hoc dimension

variables like em and x height . But pen-oriented dimensions that are deļ¬ned via

deļ¬ne blacker pixels will be slightly diļ¬erent, because cheapo mode has blacker =

240 Appendix A: Answers to All the Exercises

0.65 while luxo mode has blacker = 0.1 (since the luxo printer has diļ¬erent physi- Tobin

cal characteristics). Similarly, deļ¬ne corrected pixels (which we are just about to

discuss) will produce slightly diļ¬erent results in the two given modes.

11.2. Increasing ht # would make the letter shape and the bounding box taller;

increasing xgap # would move point 5 to the left, thereby making the middle bar shorter;

increasing u# would make the shape and its bounding box wider; increasing s# would

widen the bounding box at both sides without changing the letter shape; increasing o#

would move points 4, 5, and 6 to the right; increasing px # would make the pen thicker

(preserving the top edge of the upper bar, the bottom edge of the lower bar, and the

center of the middle bar and the stem).

11.3. The only possible surprise is the position of y1 , which should match similar

details in the ā˜ ā™ and the ā˜ ā™ of Chapter 4:

beginchar("F",14*u#+2s#,ht#,0); pickup logo_pen;

x1=x2=x3=leftstemloc; x4=w-x1+o; x5=x4-xgap;

y2=y5; y3=y4; bot y1=-o; top y3=h; y2=barheight;

draw z1--z3--z4; draw z2--z5;

labels(1,2,3,4,5); endchar;

11.4. The quantity called ss in Chapter 4 is now leftstemloc .

beginchar("M",18*u#+2s#,ht#,0); pickup logo_pen;

x1=x2=leftstemloc; x4=x5=w-x1; x3=w-x3;

y1=y5; y2=y4; bot y1=-o; top y2=h+o; y3=y1+ygap;

draw z1--z2--z3--z4--z5;

labels(1,2,3,4,5); endchar;

beginchar("T",13*u#+2s#,ht#,0); pickup logo_pen;

lft x1=0; x2=w-x1; x3=x4=.5w;

y1=y2=y3; top y1=h; bot y4=-o;

draw z1--z2; draw z3--z4;

labels(1,2,3,4); endchar;

11.5. ā˜ ā™; possibly also ā˜ ā™; and Georgia Tobin sug-

gests that ā˜ ā™ might be a legal term.

11.6. Delete the line of logo.mf that deļ¬nes barheight#, and insert that line into

each of the parameter ļ¬les logo10.mf, logo9.mf, logo8.mf. Then other bar-line heights

are possible by providing new parameter ļ¬les; another degree of āmeta-nessā has there-

fore been added to the meta-font.

11.7. (This is tricky.) Insert the lines

if known pixmag: begingroup interim hppp:=pixmag*hppp;

special "title cheapo simulation" endgroup;

extra_endchar:="currentpicture:=currentpicture scaled pixmag;"

& "w:=w*pixmag;" & extra_endchar; fi

right after ā˜mode_setupā™ in logo.mf, and also include the line

if known pixmag: hppp:=pixmag*hppp; vppp:=pixmag*vppp; fi

Appendix A: Answers to All the Exercises 241

at the very end of that ļ¬le. Then run with special

gf

\mode="cheapo"; input cheaplogo10

where the ļ¬le ā˜cheaplogo10.mfā™ says simply ā˜pixmag=10; input logo10ā™. (The interim

hppp setting and the special command are used to fool into giving the

appropriate extension to the gf ļ¬le name. Incidentally, you could print with this font on

cheapo at ten-fold magniļ¬cation if you told TEX to use the font ā˜cheaplogo10 scaled

10000ā™; but on luxo you would simply call this font ā˜cheaplogo10ā™.)

12.1. The changes are straightforward, except for the italic correction (for which a

rough estimate like the one shown here is good enough):

"Right parenthesis";

numeric ht #, dp #; ht # = body height #; .5[ht #, ā’dp #] = axis #;

beginchar (")", 7u#, ht #, dp #); italcorr axis # ā— slant ā’ .5u#;

pickup ļ¬ne.nib ; penpos1 (hair ā’ ļ¬ne , 0);

penpos2 (.75[thin , thick ] ā’ ļ¬ne , 0); penpos3 (hair ā’ ļ¬ne , 0);

lft x1l = lft x3l = u; rt x2r = x1 + 4u; top y1 = h; y2 = .5[y1 , y3 ] = axis ;

ļ¬lldraw z1l {(z2l ā’ z1l ) xscaled 3} . . . z2l . . . {(z3l ā’ z2l ) xscaled 3}z3l

- - z3r {(z2r ā’ z3r ) xscaled 3} . . . z2r . . . {(z1r ā’ z2r ) xscaled 3}z1r - - cycle;

penlabels(1, 2, 3); endchar;

We will see in Chapter 15 that itā™s possible to guarantee perfect symmetry between left

and right parentheses by using picture transformations.

12.2. When horizontal lines are being typeset, TEX keeps track of the maximum

height and maximum depth of all boxes on the line; this determines whether or not

extra space is needed between baselines. The height and depth are also used to position

an accent above or below a character, and to place symbols in mathematical formulas.

Sometimes boxes are also stacked up vertically, in which case their heights and depths

are just as important as their widths are for horizontal setting.

13.1. (4, 4), (4, 5), (5, 5), (5, 4). (Therefore the command

unļ¬ll (4, 4) - - (4, 5) - - (5, 5) - - (5, 4) - - cycle

will decrease the value of this pixel by 1.)

13.2. The result would be exactly the same; ļ¬ll and unļ¬ll commands can be given

in any order. (After an initial unļ¬ll command, some pixel values will be ā’1, the

others will be zero.)

13.3. unļ¬ll (4, 1) - - (4, 8) - - (5, 8) - - (5, 1) - - cycle.

13.4. Here are two of the many solutions:

ļ¬ll (0, 3) - - (9, 3) - - (9, 6) - - (6, 6) - - (6, 9) - -

(3, 9) - - (3, 0) - - (6, 0) - - (6, 6) - - (0, 6) - - cycle;

ļ¬ll (0, 3) - - (9, 3) - - (9, 6) - - (0, 6) - - (0, 3) - -

(3, 3) - - (3, 0) - - (6, 0) - - (6, 9) - - (3, 9) - - (3, 3) - - cycle.

(It turns out that any pixel pattern can be obtained by a single, suļ¬ciently hairy ļ¬ll

command. But unnatural commands are usually also ineļ¬cient and unreadable.)

242 Appendix A: Answers to All the Exercises

13.5. The value of the enclosed pixel is increased by 2. (Weā™ll see later that thereā™s cullit

undraw

a simpler way to do this.)

withpen

clearit

13.6. True; j ā’ k = l ā’ m, since k + l = j + m. (What comes up must go down.) withpen

addto

13.7. The tricky part is to remember that ā˜erase draw zi - - zj ā™ will erase pixels withweight

undraw

near zi and zj . Therefore if z3 - - z4 is drawn before z4 - - z2 , we canā™t erase z4 - - z2

cullit

without losing some of z3 - - z4 ; itā™s necessary to erase only part of one line. One way undraw

Leban

to solve the problem is to do the following, after deļ¬ning the points and picking up the

pen as before:

draw z3 - - z4 ; draw z5 - - z6 ;

cullit; pickup pencircle scaled 1.6pt ;

undraw z7 - - 1 [z7 , z5 ]; undraw z2 - - 1 [z2 , z4 ];

2 2

cullit; pickup pencircle scaled .4pt ;

draw z3 - - z1 - - z2 - - z4 ; draw z5 - - z7 - - z8 - - z6 ;

for k = 1 upto 4: draw zk - - zk+4 ; endfor.

(Note that it would not be quite enough to erase only from z7 to 1 [z7 , z5 ]!)

3

Itā™s also possible to solve this problem without partial erasing, if we use addi-

tional features of that havenā™t been explained yet. Letā™s consider only the

job of drawing z7 - - z5 - - z6 and z3 - - z4 - - z2 , since the other eight lines can easily be

added later. Alternative Solution 1 uses picture operations:

pen eraser ; eraser = pencircle scaled 1.6pt ;

draw z3 - - z4 ; erase draw z7 - - z5 withpen eraser ; draw z7 - - z5 ;

picture savedpicture ; savedpicture = currentpicture ; clearit;

draw z6 - - z5 ; erase draw z2 - - z4 withpen eraser ; draw z2 - - z4 ;

addto currentpicture also savedpicture .

Alternative Solution 2 is trickier, but still instructive; it uses ā˜withweightā™ options and

the fact that draw does not increase any pixel values by more than the stated weight

when the path is a straight line:

- - z4 ; undraw z7 - - z5 withpen eraser ;

draw z3

draw z7 - - z5 withweight 2; cullit withweight 2;

- - z5 ; undraw z2 - - z4 withpen eraser ;

draw z6

draw z2 - - z4 withweight 2;

(These alternative solutions were suggested by Bruce Leban.)

13.8. Hereā™s an analog of the ļ¬rst solution to the previous exercise:

beginchar ("*", 10pt #, 7pt #, 2pt #);

pair center ; . . . as in the hint

pickup pencircle scaled .4pt ; draw star ;

cullit; pickup pencircle scaled 1.6pt ;

for k = 0 upto 4: undraw subpath(k + .55, k + .7) of star ; endfor

cullit; pickup pencircle scaled .4pt ;

for k = 0 upto 4: draw subpath(k + .47, k + .8) of star ; endfor

labels(0,1,2,3,4); endchar.

Appendix A: Answers to All the Exercises 243

However, as in the previous case, thereā™s an Alternate Solution 1 by Bruce Leban that Leban

overdraw

is preferable because it doesnā™t depend on magic constants like .55 and .47:

begingroup

interim

beginchar . . . as above . . . scaled .4pt ; addto

picture savedpicture ; savedpicture = nullpicture; cull

cullit

pen eraser ; eraser := pencircle scaled 1.6pt ; also

for k = 0 upto 4: endgroup

origin

draw subpath(k, k + 1) of star ; cullit;

undraw subpath(k + 3, k + 4) of star withpen eraser ; cullit;

addto savedpicture also currentpicture ; clearit; endfor

currentpicture := savedpicture ; labels(0,1,2,3,4); endchar.

13.9. It increases pixel values by 1 in the ļ¬ve lobes of the star, and by 2 in the

central pentagon-like region.

13.10. def overdraw expr c = erase ļ¬ll c; draw c enddef .

13.11. First we need to generalize the overdraw macro of the previous exercise so

that it applies to arbitrary cycles c, even those that are self-intersecting:

def overdraw 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 ;

(This code uses operations deļ¬ned later in this chapter; it erases the region of pixels

that would be made nonzero by the command ā˜ļ¬ll cā™.) The watchband is now formed

by overdrawing its links, one at a time, doing ļ¬rst the ones that are underneath:

beginchar(M , 1.25in #, .5in #, 0); pickup pencircle scaled .4pt ;

z1 = (20, ā’13); z2 = (30, ā’6); z3 = (20, 1); z4 = (4, ā’7);

z5 = (ā’12, ā’13); z6 = (ā’24, ā’4); z7 = (ā’15, 6);

path M ; M = (origin . . z1 . . z2 . . z3 . . z4 . . z5 . . z6 . . z7 . .

origin . . ā’z7 . . ā’z6 . . ā’z5 . . ā’z4 . . ā’z3 . . ā’z2 . . ā’z1 . . cycle)

scaled (h/26) shifted (.5w, .5h);

def link(expr n) =

overdraw subpath 1 (n, n + 1) of M - -

3

subpath 1 (n + 25, n + 24) of M - - cycle enddef ;

3

for k = 1 upto 12: link(k + 11); link(12 ā’ k); endfor endchar;

13.12. The pixel pattern 1 1 is culled to 11, and needs to sort the edges

21 11

as it does this; so the result is simply

row 1: | 0+ 2-

row 0: | 0+ 2-

13.13. The pixel pattern is 1 1 + 1 1 + 1 1 ā’ 2 1 = 1 2 before the ļ¬nal rotation, with

21 21 12 11 43

the reference point at the lower left corner of the 4; after rotation it is 2 3 , with the

14

reference point at the lower right corner of the 4. Rotation causes to sort

244 Appendix A: Answers to All the Exercises

the edges, but the transition values per edge are never more than Ā±3. You werenā™t xscaled-1

expected to know about this limit of Ā±3, but it accounts for what is actually reported: yscaled-1

save

interim

row 1: | -2++ -1+ 0---

turningcheck

row 0: | -2+ -1+++ 0--- 0-

13.14. ā˜V scaled-1ā™ should be the same as ā˜V rotated 180ā™, because transformations

apply to coordinates rather than to pixel values. (Note, incidentally, that the reļ¬‚ections

ā˜V xscaled-1ā™ and ā˜V yscaled-1ā™ both work, and that ā˜V scaled-1ā™ is the same as

ā˜V xscaled-1 yscaled-1ā™.)

13.15. The result is the same as ā˜V shifted (2,3)ā™; the coordinates of a shift are

rounded to the nearest integers when a picture is being shifted.

13.16. row 3: 0+ 4- |

row 2: 0+ 4- |

row 1: 0+ 4- 0+ 2- |

row 0: 0+ 4- 0+ 2- |

(Scaling of pictures must be by an integer.)

13.17. is currently executing instructions after having read as far as line 5

of the ļ¬le expr.mf.

13.18. The pixel values of currentpicture become 1 if they were Ā±1, otherwise they

become 0.

13.19. (a) addto V1 also V2 ; cull V1 keeping (2, 2). (b) Same, but cull keeping

(1, 2). (c) Same, but cull keeping (1, 1).

13.20. Subtract one from the other, and cull the result dropping (0, 0); then test to

see if the total weight is zero.

13.21. (a) Same as ā˜draw pā™, but using q instead of the currently-picked-up pen.

(b) Same eļ¬ect as ā˜draw p; draw p; draw pā™ (but faster). (c) Same as ā˜draw p

withweight wā™, because undrawā™s ā˜withweight ā’1ā™ is overridden. (d) Same as

ā˜unļ¬lldraw c; unļ¬lldraw cā™, but using q instead of currentpen . (e) Same as ā˜erase

ļ¬lldraw cā™, because the ā˜withweight 2ā™ is overridden. [Since erase has culled all

weights to 0 or 1, thereā™s no need to ādoubly erase.ā] (f) Same eļ¬ect as ā˜cullit; addto

currentpicture also currentpicture ā™ (but faster).

13.22. vardef safeļ¬ll expr c = save region ;

picture region ; region =nullpicture;

interim turningcheck := 0;

addto region contour c; cull region dropping (0, 0);

addto currentpicture also region enddef .

13.23. cull currentpicture keeping (1, inļ¬nity );

picture v; v := currentpicture ;

cull currentpicture keeping (1, 1) withweight 3;

addto currentpicture also v ā’ v shifted right

ā’ v shifted left ā’ v shifted up ā’ v shifted down ;

cull currentpicture keeping (1, 4).

Appendix A: Answers to All the Exercises 245

13.24. (We assume that currentpicture initially has some conļ¬guration in which all directiontime

pixel values are zero or one; one means āalive.ā)

picture v; def c = currentpicture enddef ;

forever: v := c; showit;

addto c also c shifted left + c shifted right ;

addto c also c shifted up + c shifted down ;

addto c also c ā’ v; cull c keeping (5, 7); endfor.

(It is wise not to waste too much computer time watching this program.)

14.1. beginchar ("b", 5pt #, 5pt #, 0);

ļ¬ll ((0, 0) - - quartercircle - - cycle) scaled 10pt ; endchar.

1

14.2. A quartercircle corresponds to a circle whose diameter is 1; the radius is .

2

14.3. beginchar ("c", 5pt #, 5pt #, 0);

pickup pencircle scaled (.4pt + blacker );

draw quartercircle rotated 90 scaled 10pt shifted (5pt , 0); endchar.

14.4. beginchar ("d", 5pt # ā— sqrt 2, 5pt #, 0);

pickup pencircle scaled (.4pt + blacker );

draw ((0, 0) - - quartercircle - - cycle) rotated 45 scaled 10pt shifted (.5w, 0);

endchar.

14.5. beginchar ("e", 10pt #, 7.5pt #, 2.5pt #);

pickup pencircle scaled (.4pt + blacker );

for D = .2w, .6w, w: draw fullcircle scaled D shifted (.5w, .5[ā’d, h]);

endfor endchar.

The program for ā˜ ā™ is similar, but ā˜fullcircle scaled Dā™ is replaced by

unitsquare shifted ā’(.5, .5) rotated 45 scaled (D/ sqrt 2).

14.6. There are inļ¬‚ection points, because there are no bounding triangles for the

ā˜. . .ā™ operations in the superellipse macro of Appendix B, unless .5 ā¤ s ā¤ 1.

14.7. (0, 0) . . (1, 0) & (1, 0) . . (1, 1) & (1, 1) . . (0, 1) & (0, 1) . . (0, 0) & cycle.

Incidentally, if each ā˜&ā™ in this path is changed to ā˜. .ā™, we get a path that goes through

the same points; but it is a path of length 8 that comes to a complete stop at each

corner. In other words, the path remains motionless between times 1 ā¤ t ā¤ 2, 3 ā¤ t ā¤ 4,

etc. This length-8 path therefore behaves somewhat strangely with respect to the

ā˜directiontimeā™ operation. Itā™s better to use ā˜&ā™ than to repeat points of a path.

14.8. Let Ī“1 = z1 ā’ z0 , Ī“2 = z2 ā’ z1 , Ī“3 = z0 ā’ z2 ; l1 = |Ī“1 |, l2 = |Ī“2 |, l3 = |Ī“3 |;

Ļ1 = arg(Ī“2 /Ī“1 ), Ļ2 = arg(Ī“3 /Ī“2 ), Ļ3 = arg(Ī“1 /Ī“3 ). The equations to be solved are (ā—)

and (ā—ā—) for 1 ā¤ k ā¤ 3, where Ī±3 = Ī±0 and Ī²4 = Ī²1 . These six equations determine

Īø1 , Īø2 , Īø3 and Ļ1 , Ļ2 , Ļ3 .

14.9. The path is of length 9, and it is equivalent to ā˜(0, 1) - - (1, 1) - - (1, 2) - -

(0, 2) - - (0, 1){down } . . {right }(1, 0) - - (2, 0) - - (2, 1) - - (1, 1) - - (1, 0)ā™. Although

unitsquare is a cycle, the cycle is broken when it is used inside a larger path; the

resulting non-cyclic square path goes down when it ends and right when it begins.

246 Appendix A: Answers to All the Exercises

14.10. Yes; for example, ā˜z0 . . z1 . . z2 - - z3 ā™ would be equivalent to ā˜z0 . . z1 . . BernshteĖ˜

Ä±n

{curl 2}z2 {curl 2} . . {curl 2}z3 ā™. But a path like z0 - - z1 - - z2 - - z3 would not be

aļ¬ected, because all directions would turn out to be the same as before. (The path

ā˜z0 {curl a} . . {curl b}z1 ā™ is a straight line regardless of the values of a and b, because

equations (ā—ā—ā—) and (ā—ā—ā— ) always have the solution Īø0 = Ļ1 = 0 when n = 1.)

14.11. It treats this as ā˜((0, 0) . . (3, 3) . . cycle){curl 1}ā™; i.e., the part up to and

including ā˜cycleā™ is treated as a subpath (cf. ā˜p2ā™ in Chapter 8). The cycle is broken, after

which we have ā˜(0, 0) . . controls (2, ā’2) and (5, 1) . . (3, 3) . . controls (1, 5) and (ā’2, 2) . .

(0, 0){curl 1}ā™. Finally the ā˜{curl 1}ā™ is dropped, because all control points are known.

(The syntax by itself isnā™t really enough to answer this question, as you probably

realize. You also need to be told that the computation of directions and control points

is performed whenever uses the second or third alternative in the deļ¬nition

of path expression .)

14.12. True. The length of a path is the number of ā˜zk . . controls uk and vk+1 . . zk+1 ā™

segments that it contains, after all control points have been chosen.

14.13. True if 0 ā¤ t ā¤ 1, except perhaps for rounding errors; otherwise false. The

path z0 - - z1 expands into ā˜z0 . . controls 1/3[z0 , z1 ] and 2/3[z0 , z1 ] . . z1 ā™, and the

BernshteĖ˜ polynomial simpliļ¬es because t[w, w + Ī“, w + 2Ī“, w + 3Ī“] = w + 3tĪ“. In-

Ä±n

cidentally, ā˜point t of (z0 - - - z1 )ā™ is usually quite diļ¬erent from t[z0 , z1 ].

14.14. If p is a cycle, or if p is a path of length ā„ 4, the stated subpath has length 2.

Otherwise the length is max(0, length p ā’ 2).

14.15. vardef posttension expr t of p =

save q; path q;

q = point t of p {direction t of p} . . {direction t + 1 of p} point t + 1 of p;

length(postcontrol 0 of q ā’ point 0 of q)

/length(postcontrol t of p ā’ point t of p) enddef ;

vardef pretension expr t of p =

save q; path q;

q = point t ā’ 1 of p {direction t ā’ 1 of p} . . {direction t of p} point t of p;

length(precontrol 1 of q ā’ point 1 of q)

/length(precontrol t of p ā’ point t of p) enddef ;

The stated posttension turns out to be 4.54019.

14.16. The ā˜&ā™ had to be changed to ā˜. .ā™, because point t of p might not be exactly

equal to point u of q.

14.17. Since p intersects itself inļ¬nitely often at times (t, t), the task may seem im-

possible; but ā™s shuļ¬„ed-binary search procedure provides a way. Namely,

p intersectiontimes reverse p = (0.17227, 0.28339), from which we can deduce that

t = 0.17227 and 1 ā’ u = 0.28339.

15.1. (x,-y).

15.2. (x, y) rotated 180, or (x, y) scaled ā’1.

15.3. True if and only if xpart t = ypart t = 0. If the stated equation holds for

at least one pair (x, y), it holds for all (x, y). According to the syntax of Chapter 8,

Appendix A: Answers to All the Exercises 247

interprets ā˜ā’(x, y) transformed tā™ as (ā’(x, y)) transformed t. (Inciden- aļ¬ne transformations

homogeneous

tally, mathematicians call ā™s transformers āaļ¬ne transformations,ā and the

parentheses

special case in which the xpart and ypart are zero is called āhomogeneous.ā) makepath

ļ¬llin

15.4. z1 + z2 . commutativity

capsule

15.5. beginchar(126, 25u#, hheight # + border #, 0); "Dangerous left bend";

currentpicture := dbend reļ¬‚ectedabout ((.5w, 0), (.5w, h)); endchar;

The same idea can be used to create right parentheses as perfect mirror images of left

parentheses, etc., if the parentheses arenā™t slanted.

15.6. Change line 9 to

draw (z1 . . . p) rotatedaround((.5w, .5h), ā’45)

withpen pencircle scaled 3/4pt yscaled 1/3 rotated ā’15;

15.7. Replace line 10 by

pickup pencircle scaled 3/4pt yscaled 1/3 rotated ā’60;

draw (z1 . . . p) transformed t;

16.1. If there are two points zk and zk+1 with maximum y coordinate, the value of

ā˜penoļ¬set (ā’inļ¬nity , epsilon ) of pā™ will be zk and ā˜penoļ¬set (ā’inļ¬nity , ā’epsilon ) of pā™

will be zk+1 ; ā˜penoļ¬set left of pā™ will be one or the other. If thereā™s only one top point,

all three of these formulas will produce it. (Actually also allows pens to

be made with three or more vertices in a straight line. If there are more than two top

vertices, you can use penoļ¬set to discover the ļ¬rst and the last, as above; furthermore,

if you really want to ļ¬nd them all, makepath will produce a path from which they

can be deduced in a straightforward manner.)

16.2. ā˜pencircle scaled 1.06060ā™ is the diamond but ā˜pencircle scaled 1.06061ā™

is the square. (This assumes that ļ¬llin = 0. If, for example, ļ¬llin = .1, the change

doesnā™t occur until the diameter is 1.20204.) The next change is at diameter 1.5, which

gives a diamond twice the size of the ļ¬rst.

17.1. (a + 1) + (2a + 2) = 3a + 3 and (2a) + (2a + 1) = 4a + 1, respectively. The

ļ¬nal value of x in the ļ¬rst case is 2a + 2, hence a = .5x ā’ 1; expr will report the answer

as 1.5x (in terms of xā™s new value), since it has not been told about ā˜aā™. In the second

case expr will, similarly, say 2x-1.

This example shows that Ī± + Ī² is not necessarily equal to Ī² + Ī±, when Ī± and Ī²

involve group expressions. evaluates expressions strictly from left to right,

performing the statements within groups as they appear.

17.2. The save instruction gives ā˜?ā™ a fresh meaning, hence ā˜?ā™ is a numeric variable

unconnected to any other variables. When the group ends and ā˜?ā™ is restored to its

old meaning, the value of the group expression no longer has a name. (Itā™s called a

ācapsuleā if you try to show it.) Therefore the value of the group expression is a new,

nameless variable, as desired.

17.3. Itā™s a nameless pair whose xpart and ypart are equal; thus it is essentially

equivalent to ā˜whatever ā— (1, 1)ā™.

248 Appendix A: Answers to All the Exercises

17.4. ā˜v3 := begingroup save ?; picture ?; ? endgroupā™ refreshes the picture special-purpose

...

variable v3 without changing other variables like v2 . This construction works also for

pairs, pens, strings, etc.

18.1. Yes; the direction at z.j will be either left or right .

18.2. beginlogochar("A",15);

x1=.5w;

x2=x4=leftstemloc;

x3=x5=w-x2;

top y1=h+o;

(Figure A18a will be inserted here;

y2=y3=barheight; too bad you canā™t see it now.)

bot y4=bot y5=-o;

draw z4--z2--z3--z5;

super_half(2,1,3);

labels(1,2,3,4,5);

endchar;

Notice that all three calls of super_half in logo.mf are of the form ā˜super half (2, j, 3)ā™.

But it would not be good style to eliminate parameters i and k, even though super_half

is a special-purpose subroutine; that would make it too too special.

18.3. If bracket = 0 or serif darkness = 0. (Itā™s probably not a good idea to make

serif darkness = 0, because this would lead to an extreme case of the ā˜. . .ā™ triangle,

which might not be numerically stable in the presence of rounding errors.) Another

case, not really desirable, is left jut = right jut = 0.

18.4. Thatā™s a strange question. The serif routine includes a penpos that deļ¬nes

z$l , z$ , and z$r relative to each other, and it deļ¬nes the other six points relative to

them. Outside the routine the user ought to specify just one x coordinate and one

y coordinate, in order to position all of the points. This can be done either before or

after serif is called, but has an easier job if itā™s done beforehand.

18.5. Yes; see the previous exercise. (But in the program for "A" itā™s necessary to

deļ¬ne y4l and y5r , so that theta 4 and theta 5 can be calculated.)

18.6. beginchar ("H", 13u#, ht #, 0);

x1 = x2 = x5 = 3u;

x3 = x4 = x6 = w ā’ x1 ;

y1c = y3c = h; y2c = y4c = 0;

serif (1, thick , ā’90, jut , jut );

serif (2, thick , 90, jut , jut ); (Figure A18b will be inserted here; too bad

you canā™t see it now.)

serif (3, thick , ā’90, jut , jut );

serif (4, thick , 90, jut , jut );

ļ¬ll serif edge 2

- - reverse serif edge 1 - - cycle;

ļ¬ll serif edge 4

- - reverse serif edge 3 - - cycle;

penpos5 (thin , 90); penpos6 (thin , 90);

y5 = y6 = .52h; penstroke z5e - - z6e ;

penlabels(1, 2, 3, 4, 5, 6); endchar.

Appendix A: Answers to All the Exercises 249

18.7. def top serif (suļ¬x $)(expr xx , theta , left jut , right jut ) = future pen

penpos$ (xx , 0); z$a ā’ z$l = z$f ā’ z$r = (bracket /abs sind theta ) ā— dir theta ;

y$c = y$d = y$ ; x$c = x$l ā’ left jut ; x$d = x$r + right jut ;

z$b = z$l + whatever ā— dir theta = z$c + whatever ā— dir ā’phi ;

z$e = z$r + whatever ā— dir theta = z$d + whatever ā— dir phi ;

labels($a, $b, $c, $d, $e, $f ) enddef ;

def top serif edge suļ¬x $ =

(z$a . . controls z$b . . z$c

- - (ļ¬‚ex (z$c , .5[z$c , z$d ] ā’ dishing , z$d )) shifted (0, +epsilon )

- - z$d . . controls z$e . . z$f ) enddef ;

18.8. Assuming that py = 0, the eļ¬ective right stroke weight would be px Ā·sin(Īø5 ā’Ļ)

if it were drawn with one stroke of broad pen , and xxx Ā· sin Īø5 is the additional weight

corresponding to separate strokes xxx apart. The right-hand side of the equation is

the same calculation in the case of vertical strokes (Īø = 90ā—¦ ), when the stroke weight

of "I" is considered. (Since a similar calculation needs to be done for the letters K,

V, W, X, Y, and Z, it would be a good idea to embed these details in another macro.)

18.9. beginchar ("H", 13u#, ht #, 0);

x1 = x2 = x5 = 3u;

x3 = x4 = x6 = w ā’ x1 ;

y1 = y3 = h; y2 = y4 = 0;

top serif (1, xx , ā’90, jut , jut );

bot serif (2, xx , 90, jut , jut ); (Figure A18c will be inserted here; too bad

top serif (3, xx , ā’90, jut , jut );

you canā™t see it now.)

bot serif (4, xx , 90, jut , jut );

ļ¬lldraw bot serif edge 2

- - reverse top serif edge 1 - - cycle;

ļ¬ll bot serif edge 4

- - reverse top serif edge 3 - - cycle;

y5 = y6 = .52h; draw z5 - - z6 ;

penlabels(1, 2, 3, 4, 5, 6); endchar.

18.10. The replacement text contains ten tokens,

t e p

def = enddef ; def t =

where t , e , and p are placeholders for argument insertion. When this macro is

expanded with tracingmacros > 0, will type

foo(TEXT0)<expr>of<primary>->def(TEXT0)=(EXPR1)enddef;def.t=(EXPR2)

followed by the arguments (TEXT0), (EXPR1), and (EXPR2).

ńņš. 8 |