<< . .

. 19
( : 45)



. . >>

version at the end of Chapter 3:



(Figure 16d will be inserted here; too bad you can™t see it now.)




Here™s another example of cuto¬ , in which
the endpoints of ™s ˜T™ have been
—¦
cropped at 10 angles to the perpendicular of the
stroke direction:
pickup logo_pen; (Figure 16e will be inserted here;
too bad you can™t see it now.)
top lft z1=(0,h); top rt z2=(w,h);
top z3=(.5w,h); z4=(.5w,0);
draw z1--z2;
cutoff(z1,170); cutoff(z2,-10);
draw z3--z4; cutoff(z4,-80).

The cuto¬ macro of Appendix B deals with several things that we™ve been
studying recently, so it will be instructive to look at it now (slightly simpli¬ed):

def cuto¬ (expr z, theta ) =
cut pic := nullpicture;
addto cut pic doublepath z withpen currentpen ;
addto cut pic contour ((0, ’1) - - (1, ’1) - - (1, 1) - - (0, 1) - - cycle)
scaled 1.42(1 + max(’pen lft , pen rt , pen top , ’pen bot ))
rotated theta shifted z ;
cull cut pic keeping (2, 2) withweight ’1;
addto currentpicture also cut pic enddef .

The main work is done in a separate picture variable called cut pic , so that neighboring
strokes won™t be a¬ected. First cut pic is set to the full digitized pen image (by making
a doublepath from a single point). Then a rectangle that includes the cuto¬ region
is added in; pen lft , pen rt , pen top , and pen bot are the quantities used to compute
the functions lft , rt , top , and bot , so they bound the size of the pen. The culling
operation produces the intersection of pen and rectangle, which is ¬nally subtracted
from currentpicture .
152 Chapter 16: Calligraphic E¬ects


We shall conclude this chapter by studying two examples of how ™s tilde
pencircle
pen-and-curve-drawing facilities can combine in interesting ways. First, let™s
pensquare
examine two “tilde” characters Bernshte˜ polynomial
±n
serif
¬lldraw
(Figure 16f&g will be inserted here; too bad you can™t see it now.)
dishing
¬‚ex
epsilon
which were both created by a single command of the form strange paths
controls
draw z1 . . controls z2 and z3 . . z4 .
The left example was done with a pencircle xscaled .8pt yscaled .2pt rotated 50, and
the right example was exactly the same but with pensquare. The control points z2
and z3 that made this work were de¬ned by
y2 ’ y1 = y4 ’ y3 = 3(y4 ’ y1 );
z2 ’ z1 = z4 ’ z3 = whatever — dir 50.
The second pair of equations is an old calligrapher™s trick, namely to start and ¬nish
a stroke in the direction of the pen you™re holding. The ¬rst pair of equations is a
mathematician™s trick, based on the fact that the Bernshte˜ polynomial t[0, 3, ’2, 1]
±n
goes from 0 to 1 to 0 to 1 as t goes from 0 to .25 to .75 to 1.
Next, let™s try to draw a fancy serif with the same two pens, holding them at
a 20—¦ angle instead of a 50—¦ angle. Here are two examples




(Figure 16h&i will be inserted here; too bad you can™t see it now.)




that can be created by ˜¬lldraw™ commands:
¬lldraw z1 . . controls z2 . . z3
- - (¬‚ex (z3 , .5[z3 , z4 ] + dishing , z4 )) shifted (0, ’epsilon )
- - z4 . . controls z5 . . z6 - - cycle.
The dishing parameter causes a slight rise between z3 and z4 ; the ¬‚ex has been lowered
by epsilon in order to avoid the danger of “strange paths,” which might otherwise be
caused by tiny loops at z3 or z4 . But the most interesting thing about this example
is the use of double control points, z2 and z5 , in two of the path segments. (Recall
that ˜controls z2 ™ means the same thing as ˜controls z2 and z2 ™.) These points were
determined by the equations
x2 = x1 ; z2 = z3 + whatever — dir 20;
x5 = x6 ; z5 = z4 + whatever — dir ’20;
thus, they make the strokes vertical at z1 and z6 , parallel to the pen angle at z3 , and
parallel to the complementary angle at z4 .
Chapter 16: Calligraphic E¬ects 153


EVETTS
REYNOLDS




The pen, probably more than any other tool,
has had the strongest in¬‚uence upon lettering
in respect of serif design . . .
It is probable that the letters [of the Trajan column]
were painted before they were incised,
and though their main structure is attributed to the pen
and their ultimate design to the technique of the chisel,
they undoubtedly owe much of their freedom
to the in¬‚uence of the brush.
” L. C. EVETTS, Roman Lettering (1938)

Remember that it takes time, patience, critical practice
and knowledge to learn any art or craft.
No “art experience” is going to result from any busy work
for a few hours experimenting with the edged pen.
. . . Take as much time as you require,
and do not become impatient.
If it takes a month to get it,
then be happy that it takes only a month.
” LLOYD REYNOLDS, Italic Calligraphy & Handwriting (1969)
(page 154)




17
Grouping
Chapter 17: Grouping 155


We have now covered all the visual, graphic aspects of ”its points, end
program
paths, pens, and pictures; but we still don™t know everything about ™s end
organizational, administrative aspects”its programs. The next few chapters of statement list
;
this book therefore concentrate on how to put programs together e¬ectively. statements
A program is a sequence of statements separated by semi- empty statement
compound statement
colons and followed by ˜end™. More precisely, the syntax rules group
statement
program ’’ statement list end begingroup
endgroup
statement list ’’ empty | statement ; statement list save command
save
de¬ne a program in terms of a statement . symbolic token list
,
But what are statements? Well, they are of various kinds. An “equation” interim command
states that two expressions are supposed to be equal. An “assignment” assigns interim
:=
the value of an expression to a variable. A “declaration” states that certain
variables will have a certain type. A “de¬nition” de¬nes a macro. A “title”
gives a descriptive name to the character that is to follow. A “command” orders
to do some speci¬c operation, immediately. The “empty statement”
tells to do absolutely nothing. And a “compound statement” is a
list of other statements treated as a group.
statement ’’ equation | assignment | declaration
| de¬nition | title | command | empty
| begingroup statement list statement endgroup
We™ve given the syntax for equation and assignment in Chapter 10; the syntax
for declaration appeared in Chapter 7; de¬nition and title and command
will appear in later chapters. Our main concern just now is with the ¬nal type
of statement , where begingroup and endgroup bind other statements into a
unit, just as parentheses add structure to the elements of an algebraic expression.
The main purpose of grouping is to protect the values of variables in one
part of the program from being clobbered in another. A symbolic token can be
given a new meaning inside a group, without changing the meaning it had outside
that group. (Recall that deals with three basic kinds of tokens, as
discussed in Chapter 6; it is impossible to change the meaning of a numeric token
or a string token, but symbolic tokens can change meanings freely.)
There are two ways to protect the values of variables in a group. One is
called a save command , and the other is called an interim command :
save command ’’ save symbolic token list
symbolic token list ’’ symbolic token
| symbolic token list , symbolic token
interim command ’’ interim internal quantity := right-hand side
The symbolic tokens in a save command all lose their current meanings, but
those old meanings are put into a safe place and restored at the end of the current
group. Each token becomes unde¬ned, as if it had never appeared before. For
156 Chapter 17: Grouping


example, the command tag
spark
internal quantity
save x, y
beginchar
endchar
e¬ectively causes all previously known variables like x1 and y5r to become in- tracingrestores
accessible; the variable x1 could now appear in a new equation, where it would
have no connection with its out-of-group value. You could also give the silly
command
save save;
this would make the token ˜save™ itself into a tag instead of a spark , so you
couldn™t use it to save anything else until the group ended.
An interim command is more restrictive than a save, since it applies only to
an internal quantity . (Recall that internal quantities are special variables
like tracingequations that take numeric values only; a complete list of all the standard
internal quantities can be found in Chapter 25, but that list isn™t exhaustive because
you can de¬ne new ones for your own use.) treats an interim command just
like an ordinary assignment, except that it undoes the assignment when the group ends.
If you save something two or more times in the same group, the ¬rst saved
value takes precedence. For example, in the construction
begingroup
...
interim autorounding := 0; save x;
...
interim autorounding := 1; save x;
...
endgroup
the values of autorounding and x after the end of the group will be their previous values
just before the statement ˜interim autorounding := 0™. (Incidentally, these might not
be the values they had upon entry to the group).
Tokens and internal quantities regain their old meanings and values at the end
of a group only if they were explicitly saved in a save or interim command.
All other changes in meaning and/or value will survive outside the group.
The beginchar operation of plain includes a begingroup, and
endchar includes endgroup. Thus, for example, interim assignments can be
made in a program for one character without any e¬ect on other characters.
A save command that™s not in a group simply clears the meanings of the
symbolic tokens speci¬ed; their old meanings are not actually saved, because
they never will have to be restored. An interim command outside a group acts just
like a normal assignment.
If you set the internal quantity tracingrestores to a positive value,
will make a note in your transcript ¬le whenever it is restoring the former value
of a symbolic token or internal quantity. This can be useful when you™re debugging a
program that doesn™t seem to make sense.
Chapter 17: Grouping 157


Groups can also be used within algebraic expressions. This is the other group expression
whatever
important reason for grouping; it allows to do arbitrarily compli- FULTON
cated things while in the middle of other calculations, thereby greatly increasing WILLIS
the power of macro de¬nitions (which we shall study in the next chapter). A
group expression has the general form
begingroup statement list expression endgroup
and it ¬ts into the syntax of expressions at the primary level. The meaning
of a group expression is: “Perform the list of statements, then evaluate the
expression, then restore anything that was saved in this group.”
Group expressions belong in the syntax rules for each type of expression,
but they were not mentioned in previous chapters because it would have been
unnecessarily distracting. Thus, for example, the syntax for numeric primary actually
includes the additional alternative
begingroup statement list numeric expression endgroup.
The same goes for pair primary , picture primary , etc.; Chapter 25 has the complete
rules of syntax for all types of expressions.
EXERCISE 17.1
What is the value of the expression
begingroup x:=x+1; x endgroup + begingroup x:=2x; x endgroup
if x initially has the value a? What would the value have been if the two group
expressions had appeared in the opposite order? Verify your answers using the expr
routine of Chapter 8.
EXERCISE 17.2
Appendix B de¬nes whatever to be an abbreviation for the group expression
˜begingroup save ?; ? endgroup™. Why does this work?
EXERCISE 17.3
What is the value of ˜begingroup save ?; (?, ?) endgroup™ ?
EXERCISE 17.4
According to exercise 10.2, the assignment ˜x3 := whatever ™ will make the
numeric variable x3 behave like new, without a¬ecting other variables like x2 . Devise
a similar stratagem that works for arrays of picture variables.




It is often di¬cult
to account for some beginners grouping right away
and others proving almost hopeless.
” A. G. FULTON, Notes on Ri¬‚e Shooting (1913)

Rock bands prefer San Francisco groupies to New York groupies.
” ELLEN WILLIS, But Now I™m Gonna Move (1971)
(page 158)




18
De¬nitions
(also called Macros)
Chapter 18: De¬nitions (also called Macros) 159


You can often save time writing programs by letting single tokens ”
de¬nitions
stand for sequences of other tokens that are used repeatedly. For example, replacement text
Appendix B de¬nes ˜- - -™ to be an abbreviation for ˜. . tension in¬nity . .™, and parameters
arguments
this de¬nition is preloaded as part of the plain base. Programs that rotatedaround
use such de¬nitions are not only easier to write, they™re also easier to read. But capsules
:=
Appendix B doesn™t contain every de¬nition that every programmer might want; assignment
the present chapter therefore explains how you can make de¬nitions of your own.
In the simplest case, you just say
def symbolic token = replacement text enddef
and the symbolic token will henceforth expand into the tokens of the replacement
text. For example, Appendix B says
def --- = ..tension infinity.. enddef.
The replacement text can be any sequence of tokens not including ˜enddef ™; or
it can include entire subde¬nitions like ˜def . . . enddef ™, according to certain
rules that we shall explain later.
De¬nitions get more interesting when they include parameters, which
are replaced by arguments when the de¬nition is expanded. For example, Ap-
pendix B also says
def rotatedaround(expr z,theta) =
shifted -z rotated theta shifted z enddef;
this means that an expression like ˜z1 rotatedaround (z2 , 30)™ will expand into
˜z1 shifted ’z2 rotated 30 shifted z2 ™.
The parameters ˜z™ and ˜theta™ in this de¬nition could have been any
symbolic tokens whatever; there™s no connection between them and appearances
of ˜z™ and ˜theta™ outside the de¬nition. (For example, ˜z™ would ordinarily
stand for ˜(x,y)™, but it™s just a simple token here.) The de¬nition could even
have been written with “primitive” tokens as parameters, like
def rotatedaround(expr;,+) =
shifted-; rotated+shifted; enddef;
the e¬ect would be exactly the same. (Of course, there™s no point in doing such
a thing unless you are purposely trying to make your de¬nition inscrutable.)
When ˜rotatedaround™ is used, the arguments that are substituted for
z and theta are ¬rst evaluated and put into “capsules,” so that they will behave
like primary expressions. Thus, for example, ˜z1 rotatedaround (z2 + z3 , 30)™ will
not expand into ˜z1 shifted ’z2 + z3 rotated 30 shifted z2 + z3 ™”which means
something entirely di¬erent”but rather into ˜z1 shifted ’± rotated 30 shifted ±™,
where ± is a nameless internal variable that contains the value of z2 + z3 .
A capsule value cannot be changed, so an expr parameter should not appear
at the left of the assignment operator ˜:=™.
160 Chapter 18: De¬nitions (also called Macros)


Macros are great when they work, but complicated macros sometimes surprise tracingmacros
EXPR0
their creators. provides “tracing” facilities so that you can see
group expressions
what the computer thinks it™s doing, when you™re trying to diagnose the reasons for re¬‚ectedabout
unexpected behavior. If you say ˜tracingmacros := 1™, the transcript ¬le of your run save
transform
will record every macro that is subsequently expanded, followed by the values of its xxpart
arguments as soon as they have been computed. For example, ˜rotatedaround (up , 30)™ yypart
xypart
might produce the following lines of diagnostic information:
yxpart
special-purpose macros
rotatedaround(EXPR0)(EXPR1)->shifted-(EXPR0)rotated(EXPR1)sh
beginlogochar
ifted(EXPR0) expr
(EXPR0)<-(0,1)
(EXPR1)<-30
Here™s another example from Appendix B. It illustrates the usefulness of group
expressions in macro de¬nitions:
def re¬‚ectedabout (expr p, q) =
transformed begingroup
save T ; transform T ;
p transformed T = p;
q transformed T = q;
xxpart T = ’yypart T ;
xypart T = yxpart T ;
T endgroup enddef ;
thus a new transform, T , is computed in the midst of another expression, and the
macro ˜re¬‚ectedabout(p, q)™ essentially expands into ˜transformed T ™.
Some macros, like ˜rotatedaround™, are meant for general-purpose use.
But it™s also convenient to write special-purpose macros that simplify the devel-
opment of particular typefaces. For example, let™s consider the logo
from this standpoint. The program for ˜ ™ in Chapter 11 starts with
beginchar("E",14u#+2s#,ht#,0); pickup logo_pen;
and the programs for ˜ ™, ˜ ™, etc., all have almost the same beginning. Therefore
we might as well put the following de¬nition near the top of the ¬le logo.mf:
def beginlogochar(expr code, unit_width) =
beginchar(code,unit_width*u#+2s#,ht#,0);
pickup logo_pen enddef;
Then we can start the ˜ ™ by saying simply
beginlogochar("E",14);
similar simpli¬cations apply to all seven letters. Notice from this example that
macros can be used inside macros (since ˜beginchar™ and ˜pickup™ are them-
selves macros, de¬ned in Appendix B); once you have de¬ned a macro, you have
essentially extended the language. Notice also that expr parame-
ters can be expressions of any type; for example, "E" is a string, and the ¬rst
parameter of ˜rotatedaround™ is a pair.
Chapter 18: De¬nitions (also called Macros) 161


Chapter 11 didn™t give the programs for superellipse
su¬x
˜ ™ or ˜ ™. It turns out that those programs can su¬x
be simpli¬ed if we write them in terms of an aux- text
iliary subroutine called ˜super_half™. For exam-
ple, here is how the ˜ ™ is made: (Figure 18a will be inserted here; too
bad you can™t see it now.)
beginlogochar("O",15);
x1=x4=.5w; top y1=h+o; bot y4=-o;
x2=w-x3=1.5u+s; y2=y3=barheight;
super_half(2,1,3);
super_half(2,4,3);
labels(1,2,3,4); endchar;
The super_half routine is supposed to draw half of a superellipse, through three
points whose subscripts are speci¬ed.
We could de¬ne super_half as a macro with three expr parameters,
referring to the ¬rst point as ˜z[i]™, say; but there™s a better way. Parameters
to macros can be classi¬ed as su¬xes, by saying su¬x instead of expr. In this
case the actual arguments may be any su¬x , i.e., any sequence of subscripts
and tags that complete the name of a variable as explained in Chapter 7. Here™s
what super_half looks like, using this idea:
def super_half(suffix i,j,k) =
draw z.i{0,y.j-y.i}
... (.8[x.j,x.i],.8[y.i,y.j]){z.j-z.i}
... z.j{x.k-x.i,0}
... (.8[x.j,x.k],.8[y.k,y.j]){z.k-z.j}
... z.k{0,y.k-y.j} enddef;
EXERCISE 18.1

<< . .

. 19
( : 45)



. . >>