<<

. 5
( 13)



>>

For example, quartercircle is a path that represents one-fourth of a circle of
diameter 1; it runs from point (0.5, 0) to point (0, 0.5). The program
beginchar ("a", 5pt #, 5pt #, 0);
pickup pencircle scaled (.4pt + blacker );
draw quartercircle scaled 10pt ; endchar;
therefore produces the character ˜ ™ in position ˜a™ of a font.
EXERCISE 14.1
Write a program that puts a ¬lled quarter-circle ˜ ™ into font position ˜b™.
EXERCISE 14.2
Why are the ˜ ™ and ˜ ™ characters of these examples only 5 pt wide and 5 pt
high, although they are made with the path ˜quartercircle scaled 10pt ™ ?
EXERCISE 14.3
Use a rotated quarter-circle to produce ˜ ™ in font position ˜c™.

EXERCISE 14.4
Use quartercircle to produce ˜ ™ in font position ˜d™.

Plain also provides a path called halfcircle that gives you
˜ ™; this path is actually made from two quarter-circles, by de¬ning
halfcircle = quartercircle & quartercircle rotated 90.
And of course there™s also fullcircle , a complete circle of unit diameter:
fullcircle = halfcircle & halfcircle rotated 180 & cycle.
You can draw a circle of diameter D centered at (x, y) by saying
draw fullcircle scaled D shifted (x, y);
similarly, ˜draw fullcircle xscaled A yscaled B™ yields an ellipse with axes A and B.
Besides circles and parts of circles, there™s also a standard square path
called unitsquare ; this is a cycle that runs from (0, 0) to (1, 0) to (1, 1) to (0, 1)
and back to (0, 0). For example, the command ˜¬ll unitsquare ™ adds 1 to a single
pixel value, as discussed in the previous chapter.
124 Chapter 14: Paths


EXERCISE 14.5
Use fullcircle and unitsquare to produce the characters ˜ ™ and ˜ ™ in font
positions ˜e™ and ˜f™, respectively. These characters should be 10 pt wide and
10 pt tall, and their centers should be 2.5 pt above the baseline.

path branch [ ], trunk ;
branch 1 = ¬‚ex ((0, 660), (’9, 633), (’22, 610))
& ¬‚ex ((’22, 610), (’3, 622), (17, 617))
& ¬‚ex ((17, 617), (7, 637), (0, 660)) & cycle;
branch 2 = ¬‚ex ((30, 570), (10, 590), (’1, 616))
& ¬‚ex ((’1, 616), (’11, 592), (’29, 576), (’32, 562))
& ¬‚ex ((’32, 562), (’10, 577), (30, 570)) & cycle;
branch 3 = ¬‚ex ((’1, 570), (’17, 550), (’40, 535))
& ¬‚ex ((’40, 535), (’45, 510), (’60, 477))
& ¬‚ex ((’60, 477), (’20, 510), (40, 512))
& ¬‚ex ((40, 512), (31, 532), (8, 550), (’1, 570)) & cycle;
branch 4 = ¬‚ex ((0, 509), (’14, 492), (’32, 481))
& ¬‚ex ((’32, 481), (’42, 455), (’62, 430))
& ¬‚ex ((’62, 430), (’20, 450), (42, 448))
& ¬‚ex ((42, 448), (38, 465), (4, 493), (0, 509)) & cycle;
branch 5 = ¬‚ex ((’22, 470), (’23, 435), (’44, 410))
& ¬‚ex ((’44, 410), (’10, 421), (35, 420))
& ¬‚ex ((35, 420), (15, 455), (’22, 470)) & cycle;
branch 6 = ¬‚ex ((18, 375), (9, 396), (5, 420))
& ¬‚ex ((5, 420), (’5, 410), (’50, 375), (’50, 350))
& ¬‚ex ((’50, 350), (’25, 375), (18, 375)) & cycle;
branch 7 = ¬‚ex ((0, 400), (’13, 373), (’30, 350))
& ¬‚ex ((’30, 350), (0, 358), (30, 350))
& ¬‚ex ((30, 350), (13, 373), (0, 400)) & cycle;
branch 8 = ¬‚ex ((50, 275), (45, 310), (3, 360))
& ¬‚ex ((3, 360), (’20, 330), (’70, 300), (’100, 266))
& ¬‚ex ((’100, 266), (’75, 278), (’60, 266))
& ¬‚ex ((’60, 266), (0, 310), (50, 275)) & cycle;
branch 9 = ¬‚ex ((10, 333), (’15, 290), (’43, 256))
& ¬‚ex ((’43, 256), (8, 262), (58, 245))
& ¬‚ex ((58, 245), (34, 275), (10, 333)) & cycle;
branch 10 = ¬‚ex ((8, 262), (’21, 249), (’55, 240))
(Figure 14a will be inserted here;
too bad you can™t see it now.)
& ¬‚ex ((’55, 240), (’51, 232), (’53, 220))
& ¬‚ex ((’53, 220), (’28, 229), (27, 235))
& ¬‚ex ((27, 235), (16, 246), (8, 262)) & cycle;
branch 11 = ¬‚ex ((0, 250), (’25, 220), (’70, 195))
& ¬‚ex ((’70, 195), (’78, 180), (’90, 170))
& ¬‚ex ((’90, 170), (’5, 188), (74, 183))
& ¬‚ex ((74, 183), (34, 214), (0, 250)) & cycle;
branch 12 = ¬‚ex ((8, 215), (’35, 175), (’72, 155))
& ¬‚ex ((’72, 155), (’75, 130), (’92, 110), (’95, 88))
& ¬‚ex ((’95, 88), (’65, 117), (’54, 104))
& ¬‚ex ((’54, 104), (10, 151), (35, 142))
. . ¬‚ex ((42, 130), (60, 123), (76, 124))
& ¬‚ex ((76, 124), (62, 146), (26, 180), (8, 215)) & cycle;
trunk = (0, 660) - - - (’12, 70) . . {curl 5}(’28, ’8)
& ¬‚ex ((’28, ’8), (’16, ’4), (’10, ’11))
& ¬‚ex ((’10, ’11), (0, ’5), (14, ’10))
& ¬‚ex ((14, ’10), (20, ’6), (29, ’11))
& (29, ’11){curl 4} . . (10, 100) - - - cycle;
Chapter 14: Paths 125


Sometimes it™s necessary to draw rather complicated curves, and plain ¬‚ex
El Palo Alto
provides a ˜¬‚ex ™ operation that can simplify this task. The construc- Stanford University
tion ˜¬‚ex (z1 , z2 , z3 )™ stands for the path ˜z1 . . z2 {z3 ’ z1 } . . z3 ™, and similarly
˜¬‚ex (z1 , z2 , z3 , z4 )™ stands for ˜z1 . . z2 {z4 ’ z1 } . . z3 {z4 ’ z1 } . . z4 ™; in general
¬‚ex (z1 , z2 , . . . , zn’1 , zn )
is an abbreviation for the path
z1 . . z2 {zn ’ z1 } . . · · · . . zn’1 {zn ’ z1 } . . zn .
The idea is to specify two endpoints, z1 and zn , together with one or more
intermediate points where the path is traveling in the same direction as the
straight line from z1 to zn ; these intermediate points are easy to see on a typical
curve, so they are natural candidates for key points.
For example, the command
¬ll ¬‚ex (z1 , z2 , z3 ) & ¬‚ex (z3 , z4 , z5 )
& ¬‚ex (z5 , z6 , z7 ) & ¬‚ex (z7 , z8 , z9 , z1 ) & cycle
will ¬ll the shape




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




after the points z1 , . . . , z9 have been suitably de¬ned. This shape occurs as
the fourth branch from the top of “El Palo Alto,” a tree that is often used to
symbolize Stanford University. The thirteen paths on the opposite page were
de¬ned by simply sketching the tree on a piece of graph paper, then reading o¬
approximate values of key points “by eye” while typing the code into a computer.
(A good radio or television program helps to stave o¬ boredom when you™re
typing a bunch of data like this.) The entire ¬gure involves a total of 47 ¬‚exes,
most of which are pretty mundane; but branch 12 does contain an interesting
subpath of the form
¬‚ex (z1 , z2 , z3 ) . . ¬‚ex (z4 , z5 , z6 ),
which is an abbreviation for
z1 . . z2 {z3 ’ z1 } . . z3 . . z4 . . z5 {z6 ’ z4 } . . z6 .
Since z3 = z4 in this example, a smooth curve runs through all six points,
although two di¬erent ¬‚exes are involved.
126 Chapter 14: Paths


Once the paths have been de¬ned, it™s easy to use them un¬ll
superellipse
to make symbols like the white-on-black medallion shown here: ellipse
superness
beginchar ("T", .5in #, 1.25in #, 0); Lam´ e
De¬ne the thirteen paths on the preceding pages ; Hein
(Figure
14aa will Gardner
¬ll superellipse ((w, .5h), (.5w, h), (0, .5h), (.5w, 0), .8); be inserted
fullcircle
here; too
branch 0 = trunk ; bad you
can™t see it
for n = 0 upto 12: now.)

un¬ll branch [n] shifted (150, 50) scaled (w/300);
endfor endchar;
The oval shape that encloses this tree is a superellipse , which is another special
kind of path provided by plain . To get a general shape of this kind,
you can write
superellipse (right point , top point , left point , bottom point , superness )
where ˜superness ™ controls the amount by which the curve di¬ers from a true
ellipse. For example, here are four superellipses, drawn with varying amounts of
superness, using a pencircle xscaled 0.7pt yscaled 0.2pt rotated 30:



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




The superness should be between 0.5 (when you get a diamond) and 1.0 (when
you get a square); values in the vicinity of 0.75 are usually preferred. The zero
symbol ˜0™ in this book™s typewriter font was drawn as a superellipse of superness
2’.5 ≈ .707, which corresponds to a normal ellipse; the uppercase letter ˜O™ was
drawn with superness 2’.25 ≈ .841, to help distinguish it from the zero. The
ambiguous symbol ˜¼™ (which is not in the font, but can of course
draw it) lies between these two extremes; its superness is 0.77.
A mathematical superellipse satis¬es the equation |x/a|β + |y/b|β = 1, for
some exponent β. It has extreme points (±a, 0) and (0, ±b), as well as the
“corner” points (±σa, ±σb), where σ = 2’1/β is the superness. The tangent to the
curve at (σa, σb) runs in the direction (’a, b), hence it is parallel to a line from (a, 0)
to (0, b). Gabriel Lam´ invented the superellipse in 1818, and Piet Hein popularized the
e
special case β = 2.5 [see Martin Gardner, Mathematical Carnival (New York: Knopf,
1975), 240“254]; this special case corresponds to a superness of 2’.4 ≈ .7578582832552.
™s superellipse routine does not produce a perfect superellipse, nor
Plain
does fullcircle yield a true circle, but the results are close enough for practical purposes.
EXERCISE 14.6
Try superellipse with superness values less than 0.5 or greater than 1.0; explain
why you get weird shapes in such cases.
Chapter 14: Paths 127


Let™s look now at the symbols that are used between key points, when ..
...
we specify a path. There are ¬ve such tokens in plain : “

.. free curve; ampersand
¬‚ex
... bounded curve; autorounding
-- straight line;
--- “tense” line;
& splice.
In general, when you write ˜z0 . . z1 . . etc. . . zn’1 . . zn ™, will
compute the path of length n that represents its idea of the “most pleasing
curve” through the given points z0 through zn . The symbol ˜. . .™ is essentially
the same as ˜. .™ , except that it con¬nes the path to a bounding triangle whenever
possible, as explained in Chapter 3. A straight line segment ˜zk’1 - - zk ™ usually
causes the path to change course abruptly at zk’1 and zk . By contrast, a segment
speci¬ed by ˜zk’1 - - - zk ™ will be a straight line that blends smoothly with the
neighboring curves; i.e., the path will enter zk’1 and leave zk in the direction of
zk ’ zk’1 . (The trunk of El Palo Alto makes use of this option, and we have
also used it to draw the signboard of the dangerous bend symbol at the end of
Chapter 12.) Finally, the ˜&™ operation joins two independent paths together
at a common point, just as ˜&™ concatenates two strings together.
Here, for example, is a somewhat silly path that illustrates all ¬ve basic
types of joinery:



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




z0 = (0, 100); z1 = (50, 0); z2 = (180, 0);
for n = 3 upto 9: z[n] = z[n ’ 3] + (200, 0); endfor
draw z0 . . z1 - - - z2 . . . {up }z3
& z3 . . z4 - - z5 . . . {up }z6
& z6 . . . z7 - - - z8 . . {up }z9 .
The ˜. . .™ operation is usually used only when one or both of the adjacent
directions have been speci¬ed (like ˜{up }™ in this example). Plain -
™s ¬‚ex construction actually uses ˜. . .™ , not ˜. .™ as stated earlier, because this
avoids in¬‚ection points in certain situations.

A path like ˜z0 - - - z1 - - - z2 ™ is almost indistinguishable from the broken
line ˜z0 - - z1 - - z2 ™, except that if you enlarge the former path you will see
that its lines aren™t perfectly straight; they bend just a little, so that the curve is
“smooth” at z1 although there™s a rather sharp turn there. (This means that the
autorounding operations discussed in Chapter 24 will apply.) For example, the path
128 Chapter 14: Paths


(0, 3) - - - (0, 0) - - - (3, 0) is equivalent to unitsquare
tensepath
curl
(0, 3) . . controls (’0.0002, 2.9998) and (’0.0002, 0.0002)
. . (0, 0) . . controls (0.0002, ’0.0002) and (2.9998, ’0.0002) . . (3, 0)

while (0, 3) - - (0, 0) - - (3, 0) consists of two perfectly straight segments:

(0, 3) . . controls (0, 2) and (0, 1)
. . (0, 0) . . controls (1, 0) and (2, 0) . . (3, 0).

EXERCISE 14.7
™s unitsquare path is de¬ned to be ˜(0, 0) - - (1, 0) - - (1, 1) - -
Plain
(0, 1) - - cycle™. Explain how the same path could have been de¬ned using only ˜. .™
and ˜&™, not ˜- -™ or explicit directions.

Sometimes it™s desirable to take a path and change all its connecting links
to ˜- - -™, regardless of what they were originally; the key points are left un-
has a tensepath operation that does this. For example,
changed. Plain
tensepath unitsquare = (0, 0) - - - (1, 0) - - - (1, 1) - - - (0, 1) - - - cycle.

When is deciding what curves should be drawn in place of
˜. .™ or ˜. . .™, it has to give special consideration to the beginning and ending points,
so that the path will start and ¬nish as gracefully as possible. The solution that
usually works out best is to make the ¬rst and last path segments very nearly
the same as arcs of circles; an unadorned path of length 2 like ˜z0 . . z1 . . z2 ™
will therefore turn out to be a good approximation to the unique circular arc
that passes through (z0 , z1 , z2 ), except in extreme cases. You can change this
default behavior at the endpoints either by specifying an explicit direction or by
specifying an amount of “curl.” If you call for curliness less than 1, the path will
decrease its curvature in the vicinity of the endpoint (i.e., it will begin to turn
less sharply); if you specify curliness greater than 1, the curvature will increase.
(See the de¬nition of El Palo Alto™s trunk , earlier in this chapter.)
Here, for example, are some pairs of parentheses that were drawn using
various amounts of curl. In each case the shape was drawn by a statement of the
form ˜penstroke z0e {curl c} . . z1e . . {curl c}z2e ™; di¬erent values of c produce
di¬erent-looking parentheses:


½¾ ¿
curl value 0 1 2 4 in¬nity
yields
(The parentheses of Computer Modern typefaces are de¬ned by the somewhat
more general scheme described in Chapter 12; explicit directions are speci¬ed at
the endpoints, instead of curls, because this produces better results in unusual
cases when the characters are extremely tall or extremely wide.)
The amount of curl should not be negative. When the curl is very large,
doesn™t actually make an extremely sharp turn at the endpoint;
instead, it changes the rest of the path so that there is comparatively little curvature
at the neighboring point.
Chapter 14: Paths 129


Chapter 3 points out that we can change ™s default curves by tension
path primary
specifying nonstandard “tension” between points, or even by specifying ex-
(
plicit control points to be used in the four-point method. Let us now study the full )
syntax of path expressions, so that we can come to a complete understanding of the reverse
subpath
paths that is able to make. Here are the general rules: of
path secondary
path primary ’’ pair primary | path variable path tertiary
| ( path expression ) path expression
| reverse path primary cycle
path subexpression
| subpath pair expression of path primary path join
path secondary ’’ pair secondary | path primary direction speci¬er
| path secondary transformer “
curl
path tertiary ’’ pair tertiary | path secondary ˝
path expression ’’ pair expression | path tertiary “
˝
| path subexpression direction speci¬er “
| path subexpression path join cycle ,
path subexpression ’’ path expression ˝
basic path join
| path subexpression path join path tertiary &
path join ’’ direction speci¬er basic path join direction speci¬er ..
direction speci¬er ’’ empty ..
..
| { curl numeric expression } ..
| { pair expression } ..
tension
| { numeric expression , numeric expression } tension
basic path join ’’ & | .. | .. tension .. | .. controls .. tension
tension ’’ tension tension amount and
tension amount
| tension tension amount and tension amount atleast
tension amount ’’ numeric primary controls
| atleast numeric primary controls
controls
controls ’’ controls pair primary and
| controls pair primary and pair primary up

The operations ˜. . .™ and ˜- -™ and ˜- - -™ are conspicuously absent from this syntax; that
is because Appendix B de¬nes them as macros:
... is an abbreviation for ˜. . tension atleast 1 . .™ ;
is an abbreviation for ˜{curl 1} . . {curl 1}™ ;
--
is an abbreviation for ˜. . tension in¬nity . .™ .
---
These syntax rules specify a wide variety of possibilities, even though they
don™t mention ˜- -™ and such things explicitly, so we shall now spend a little
while looking carefully at their implications. A path expression essentially has the form
···
p0 j1 p1 j2 jn pn
where each pk is a tertiary expression of type pair or path, and where each jk is a “path
join.” A path join begins and ends with a “direction speci¬er,” and has a “basic path
join” in the middle. A direction speci¬er can be empty, or it can be ˜{curl c}™ for some
c ≥ 0, or it can be a direction vector enclosed in braces. For example, ˜{up }™ speci¬es
de¬nes up to be the pair (0, 1). This
an upward direction, because plain
same direction could be speci¬ed by ˜{(0, 1)}™ or ˜{(0, 10)}™, or without parentheses as
˜{0, 1}™. If a speci¬ed direction vector turns out to be (0, 0), behaves as
130 Chapter 14: Paths


if no direction had been speci¬ed; i.e., ˜{0, 0}™ is equivalent to ˜ empty ™. An empty tension
Hobby
direction speci¬er is implicitly ¬lled in by rules that we shall discuss later.
A basic path join has three essential forms: (1) ˜&™ simply concatenates
two paths, which must share a common endpoint. (2) ˜. . tension ± and β . .™
means that a curve should be de¬ned, having respective “tensions” ± and β. Both ±
and β must be equal to 3/4 or more; we shall discuss tension later in this chapter.
(3) ˜. . controls u and v . .™ de¬nes a curve with intermediate control points u and v.
Special abbreviations are also allowed, so that the long forms of basic path
joins can usually be avoided: ˜. .™ by itself stands for ˜. . tension 1 and 1 . .™ ,
while ˜. . tension ± . .™ stands for ˜. . tension ± and ± . .™ , and ˜. . controls u . .™ stands for
˜. . controls u and u . .™ .
Our examples so far have always constructed paths from points; but the syn-
tax shows that it™s also possible to write, e.g., ˜p0 . . p1 . . p2 ™ when the p™s
themselves are paths. What does this mean? Well, every such path will already
have been changed into a sequence of curves with explicit control points; -
expands such paths into the corresponding sequence of points and basic path
joins of type (3). For example, ˜((0, 0) . . (3, 0)) . . (3, 3)™ is essentially the same as
˜(0, 0) . . controls (1, 0) and (2, 0) . . (3, 0) . . (3, 3)™, because ˜(0, 0) . . (3, 0)™ is the path
˜(0, 0) . . controls (1, 0) and (2, 0) . . (3, 0)™. If a cycle is expanded into a subpath in this
way, its cyclic nature will be lost; its last point will simply be a copy of its ¬rst point.
Now let™s consider the rules by which empty direction speci¬ers can inherit
speci¬cations from their environment. An empty direction speci¬er at the
beginning or end of a path, or just next to the ˜&™ operator, is e¬ectively replaced by
˜{curl 1}™. This rule should be interpreted properly with respect to cyclic paths, which
have no beginning or end; for example, ˜z0 . . z1 & z1 . . z2 . . cycle™ is equivalent to
˜z0 . . z1 {curl 1}&{curl 1}z1 . . z2 . . cycle™.
If there™s a nonempty direction speci¬er after a point but not before it, the
nonempty one is copied into both places. Thus, for example, ˜. . z{w}™ is
treated as if it were ˜. . {w}z{w}™. If there™s a nonempty direction speci¬er before a
point but not after it, the nonempty one is, similarly, copied into both places, except
if it follows a basic path join that gives explicit control points. The direction speci¬er
that immediately follows ˜. . controls u and v . .™ is always ignored.
An empty direction speci¬er next to an explicit control point inherits the direc-
tion of the adjacent path segment. More precisely, ˜. . z . . controls u and v . .™
is treated as if it were ˜. . {u ’ z}z . . controls u and v . .™ if u = z, or as if it were
˜. . {curl 1}z . . controls u and v . .™ if u = z. Similarly, ˜. . controls u and v . . z . .™ is
treated as if z were followed by {z ’ v} if z = v, by {curl 1} otherwise.
After the previous three rules have been applied, we might still be left with
cases in which there are points surrounded on both sides by empty direction
speci¬ers. must choose appropriate directions at such points, and it does
so by applying the following algorithm due to John Hobby [Discrete and Computational
Geometry 1 (1986), 123“140]: Given a sequence
z0 {d0 } . . tension ±0 and β1 . . z1 . . tension ±1 and β2 . . z2
etc. zn’1 . . tension ±n’1 and βn . . {dn }zn
Chapter 14: Paths 131


for which interior directions need to be determined, we will regard the z™s as if they mock curvature
were complex numbers. Let lk = |zk ’ zk’1 | be the distance from zk’1 to zk , and let
ψk = arg((zk+1 ’ zk )/(zk ’ zk’1 )) be the turning angle at zk . We wish to ¬nd direction
vectors w0 , w1 , . . . , wn so that the given sequence can e¬ectively be replaced by
z0 {w0 } . . tension ±0 and β1 . . {w1 }z1 {w1 } . . tension ±1 and β2 . . {w2 }z2
etc. zn’1 {wn’1 } . . tension ±n’1 and βn . . {wn }zn .
Since only the directions of the w™s are signi¬cant, not the magnitudes, it su¬ces to
determine the angles θk = arg(wk /(zk+1 ’ zk )). For convenience, we also let φk =
arg((zk ’ zk’1 )/wk ), so that
θk + φk + ψk = 0. (—)
Hobby™s paper introduces the notion of “mock curvature” according to which the fol-
lowing equations should hold at interior points:
2 ’1 ’1 2 ’1 ’1
βk lk (±k’1 (θk’1 + φk ) ’ 3φk ) = ±k lk+1 (βk+1 (θk + φk+1 ) ’ 3θk ). (——)
We also need to consider boundary conditions. If d0 is an explicit direction vector w0 ,
we know θ0 ; otherwise d0 is ˜curl γ0 ™ and we set up the equation
’1 ’1
2 2
±0 (β1 (θ0 + φ1 ) ’ 3θ0 ) = γ0 β1 (±0 (θ0 + φ1 ) ’ 3φ1 ). (———)
If dn is an explicit vector wn , we know φn ; otherwise dn is ˜curl γn ™ and we set
’1
2 2 ’1
βn (±n’1 (θn’1 + φn ) ’ 3φn ) = γn ±n’1 (βn (θn’1 + φn ) ’ 3θn’1 ). (——— )
It can be shown that the conditions ±k ≥ 3/4, βk ≥ 3/4, γk ≥ 0 imply that there is a
unique solution to the system of equations consisting of (—) and (——) for 0 < k < n plus
the two boundary equations; hence the desired quantities θ0 , . . . , θn’1 and φ1 , . . . , φn
are uniquely determined. (The only exception is the degenerate case n = γ0 γ1 = 1.)
A similar scheme works for cycles, when there is no ˜{d0 }™ or ˜{dn }™. In this
case equations (—) and (——) hold for all k.
EXERCISE 14.8
Write out the equations that determine the directions chosen for the general
cycle ˜z0 . . tension ±0 and β1 . . z1 . . tension ±1 and β2 . . z2 . . tension ±2 and β3 . . cycle™
of length 3. (You needn™t try to solve the equations.)
Whew ” these rules have determined the directions at all points. To com-
plete the job of path speci¬cation, we need merely explain how to change a
segment like ˜z0 {w0 } . . tension ± and β . . {w1 }z1 ™ into a segment of the form ˜z0 . .
controls u and v . . z1 ™ ; i.e., we ¬nally want to know ™s magic recipe for
choosing the control points u and v. If θ = arg(w0 /(z1 ’z0 )) and φ = arg((z1 ’z0 )/w1 ),
the control points are
v = z1 ’ e’iφ (z1 ’ z0 )f (φ, θ)/β,
u = z0 + eiθ (z1 ’ z0 )f (θ, φ)/±,
where f (θ, φ) is another formula due to John Hobby:
√ 1 1
2 + 2 (sin θ ’ 16 sin φ)(sin φ ’ 16 sin θ)(cos θ ’ cos φ)
√ √
f (θ, φ) = .
3 (1 + 1 ( 5 ’ 1) cos θ + 1 (3 ’ 5 ) cos φ)
2 2
132 Chapter 14: Paths


There™s yet one more complication. If the tensions ± and/or β have been atleast
bounding triangle
preceded by the keyword ˜atleast™, the values of ± and/or β are increased, if
tension
necessary, to the minimum values such that u and v do not lie outside the “bounding unitsquare
triangle,” which is discussed near the end of Chapter 3. reverse

What do these complex rules imply, for users who aren™t “into”
mathematics? The most important fact is that the rules for paths are invariant
under shifting, scaling, and rotation. In other words, if the key points zk of a path are
all shifted, scaled, and/or rotated in the same way, the resulting path will be the same as
you would get by shifting, scaling, and/or rotating the path de¬ned by the unmodi¬ed
zk ™s (except of course for possible rounding errors). However, this invariance property
does not hold if the points or paths are xscaled and yscaled by separate amounts.
Another consequence of the rules is that tension speci¬cations have a fairly
straightforward interpretation in terms of control points, when the adjacent
directions have been given: The formulas for u and v simply involve division by ± and β.
This means, for example, that a tension of 2 brings the control points halfway in towards
the neighboring key points, and a tension of in¬nity makes the points very close indeed;
contrariwise, tensions less than 1 move the control points out.
Tension and curl speci¬cations also in¬‚uence ™s choices of direc-
tions at the key points. That is why, for example, the construction ˜zk’1 - - - zk ™
(which means ˜zk’1 . . tension in¬nity . . zk ™ ) a¬ects the direction of a larger path as it
enters zk’1 and leaves zk .
The rules imply that a change in the position of point zn causes a change
in the curve near point z0 , when has to choose directions at all
points between z0 and zn . However, this e¬ect is generally negligible except in the
vicinity of the changed point. You can verify this by looking, for example, at the
control points that chooses for the path ˜(0, 0) . . (1, 0) . . (2, 0) . . (3, 0) . .
(4, 0) . . . {up }(5, y)™, as y varies.
EXERCISE 14.9
Run on the ˜expr™ ¬le of Chapter 8, and ask to see the path
expression ˜unitsquare shifted (0, 1) . . unitsquare shifted (1, 0)™. Account for the
results that you get.
EXERCISE 14.10
™s abbreviation for ˜{curl 1} . . {curl 1}™.
We™ve said that ˜- -™ is plain
Would there be any essential di¬erence if ˜- -™ were de¬ned to mean ˜{curl 2} . . {curl 2}™ ?
EXERCISE 14.11
Look closely at the syntax of path expression and explain what
does with the speci¬cation ˜(0, 0) . . (3, 3) . . cycle{curl 1}™.
Now let™s come back to simpler topics relating to paths. Once a path has
been speci¬ed, there are lots of things you can do with it, besides drawing and
¬lling and suchlike. For example, if p is a path, you can reverse its direction by saying
˜reverse p™; the reverse of ˜z0 . . controls u and v . . z1 ™ is ˜z1 . . controls v and u . . z0 ™.
EXERCISE 14.12
True or false: length reverse p = length p, for all paths p.
Chapter 14: Paths 133


It™s convenient to associate “time” with paths, by imagining that we move time
subpaths
along a path of length n as time passes from 0 to n. (Chapter 8 has already
mediation
illustrated this notion, with respect to an almost-but-not-quite-circular path called p2; Bernshte˜
±n
it™s a good idea to review the discussion of paths and subpaths in Chapter 8 now before
you read further.) Given a path
p = z0 . . controls u0 and v1 . . z1 etc. zn’1 . . controls un’1 and vn . . zn
determines ˜point t of p™ as follows: If t ¤ 0, the result
and a number t,
is z0 ; if t ≥ n, the result is zn ; otherwise if k ¤ t < k + 1, it is (t ’ k)[zk , uk , vk+1 , zk+1 ],
where we generalize the ˜t[±, β]™ notation so that t[±, β, γ] means t[t[±, β], t[β, γ]] and
t[±, β, γ, δ] means t[t[±, β, γ], t[β, γ, δ]]. (This is a Bernshte˜ polynomial in t, cf. Chap-
±n
ter 3.) Given a cyclic path
c = z0 . . controls u0 and v1 . . z1 etc. zn’1 . . controls un’1 and vn . . cycle
and a number t, determines ˜point t of c™ in essentially the same way,
except that t is ¬rst reduced modulo n so as to lie in the range 0 ¤ t < n.
EXERCISE 14.13
True or false: point t of (z0 - - z1 ) = t[z0 , z1 ].
Given a path p and two time values t1 ¤ t2 , ˜subpath (t1 , t2 ) of p™ contains
all the values ˜point t of p™ as t varies from t1 to t2 . There™s no problem
understanding how to de¬ne this subpath when t1 and t2 are integers; for example,
subpath (2, 4) of p = z2 . . controls u2 and v3 . . z3 . . controls u3 and v4 . . z4
in the notation above, if we assume that n ≥ 4. The fractional case is handled by
“stretching time” in one segment of the curve; for example, if 0 < t < 1 we have
subpath (0, t) of p = z0 . . controls t[z0 , u0 ] and t[z0 , u0 , v1 ] . . t[z0 , u0 , v1 , z1 ];
subpath (t, 1) of p = t[z0 , u0 , v1 , z1 ] . . controls t[u0 , v1 , z1 ] and t[v1 , z1 ] . . z1 .
These two subpaths together account for all points of ˜z0 . . controls u0 and v1 . . z1 ™. To
get subpath (t1 , t2 ) of p when 0 < t1 < t2 < 1, applies this construction
twice, by computing subpath (t1 /t2 , 1) of subpath (0, t2 ) of p.
The operation ˜subpath (t1 , t2 ) of p™ is de¬ned for all combinations of times
(t1 , t2 ) and paths p by the following rules: Let n = length p. (1) If t1 > t2 ,
subpath (t1 , t2 ) of p = reverse subpath (t2 , t1 ) of p. Henceforth we shall assume that
t1 ¤ t2 . (2) If t1 = t2 , subpath (t1 , t2 ) of p = point t1 of p, a path of length zero.
Henceforth we shall assume that t1 < t2 . (3) If t1 < 0 and p is a cycle, subpath (t1 , t2 )
of p = subpath (t1 + n, t2 + n) of p. If t1 < 0 and p is not a cycle, subpath (t1 , t2 ) of p =
subpath (0, max(0, t2 )) of p. Henceforth we shall assume that t1 ≥ 0. (4) If t1 ≥ n and
p is a cycle, subpath (t1 , t2 ) of p = subpath (t1 ’ n, t2 ’ n) of p. If t1 < n < t2 and p is a
cycle, subpath (t1 , t2 ) of p = subpath (t1 , t2 ) of (p & p & cycle). If t2 > n and p is not a
cycle, subpath (t1 , t2 ) of p = subpath (min(t1 , n), n) of p. Henceforth we shall assume
that 0 ¤ t1 < t2 ¤ n. (5) If t1 ≥ 1, subpath (t1 , t2 ) of p = subpath (t1 ’ 1, t2 ’ 1) of
subpath (1, n) of p, where subpath (1, n) of p is obtained by removing the ¬rst segment
of p. Henceforth we shall assume that 0 ¤ t1 < 1. (6) If t2 > 1, subpath (t1 , t2 )
of p = subpath (t1 , 1) of p & subpath (1, t2 ) of p. Henceforth we shall assume that
0 ¤ t1 < t2 ¤ 1. (7) The remaining cases were de¬ned in the preceding paragraph.
134 Chapter 14: Paths


EXERCISE 14.14 postcontrol
precontrol
What is the length of ˜subpath (2.718, 3.142) of p™ ?
interpath
interpolate between paths
Besides ˜point t of p™, allows you to speak of ˜postcontrol t of p™
Knuth, D E
and ˜precontrol t of p™; this gives access to the control points of a path. Let Knuth, J C
valentine
p = z0 . . controls u0 and v1 . . z1 etc. zn’1 . . controls un’1 and vn . . zn . heart

If t < n, postcontrol t of p is the ¬rst control point in subpath (t, n) of p; if t ≥ n,
postcontrol t of p is zn . If t > 0, precontrol t of p is the last control point in subpath (0, t)
of p; if t ¤ 0, precontrol t of p is z0 . In particular, if t is an integer, postcontrol t of p
is ut for 0 ¤ t < n, and precontrol t of p is vt for 0 < t ¤ n.
The ability to extract key points and control points makes it possible to de¬ne
™s interpath function, which
interesting operations such as plain
allows you to interpolate between paths. For example, ˜interpath (1/3, p, q)™ will produce
a path of length n whose points are 1/3[point t of p, point t of q] for 0 ¤ t ¤ n, given
any paths p and q of length n. It can be de¬ned by a fairly simple program:
vardef interpath (expr a, p, q) =
for t = 0 upto length p ’ 1: a[point t of p, point t of q]
. . controls a[postcontrol t of p, postcontrol t of q]
and a[precontrol t + 1 of p, precontrol t + 1 of q] . . endfor
if cycle p: cycle % assume that p, q are both cycles or both noncycles
else: a[point in¬nity of p, point in¬nity of q] ¬ enddef ;
On February 14, 1979, the author bought a box of chocolates and placed the
box on a piece of graph paper (after suitably disposing of the contents). The
experimental data gathered in this way led to a “de¬nitive” heart shape:
heart = (100, 162) . . (140, 178){right } . . (195, 125){down }
. . (100, 0){curl 0} . . {up }(5, 125) . . {right }(60, 178) . . (100, 162);
It is interesting to interpolate between heart and other paths, by using a program like
for n = 0 upto 10: draw interpath (n/10, p, heart ); endfor.
For example, the left illustration below was obtained by taking
p = (100, 0) - - (300, 0) - - (200, 0) - - (100, 0) - - (0, 0) - - (’100, 0) - - (100, 0);
notice that interpath doesn™t necessarily preserve smoothness at the key points. The
right illustration was obtained by duplicating point (100, 0) in heart (thereby making
it a path of length 7) and taking
p = (100, 200) - - (200, 200) - - (200, 100)
- - (200, 0) - - (0, 0) - - (0, 100) - - (0, 200) - - (100, 200).




(Figure 14bb&cc will be inserted here; too bad you can™t see it now.)
Chapter 14: Paths 135


Plain allows you to say ˜direction t of p™ in order to determine the direction
directiontime
direction in which path p is moving at time t. This is simply an abbreviation
dir
for ˜(postcontrol t of p) ’ (precontrol t of p)™. Sometimes a path veers abruptly and has angle
no unique direction; in this case the direction function gives a result somewhere between epsilon
directionpoint
the two possible extremes. For example, the heart path above turns a corner at time 3; fullcircle
˜direction 3 of heart ™ turns out to be (’93.29172, 0), but ˜direction 3 ’ epsilon of heart ™
is (’46.64589, ’31.63852) and ˜direction 3 + epsilon of heart ™ is (’46.64589, 31.63852).

Conversely, can tell you when a path heads in a given direction.
You just ask for ˜directiontime w of p™, where w is a direction vector and p is
a path. This operation is best understood by looking at examples, so let™s resume our
dialog with the computer by applying to the ˜expr™ ¬le as in Chapter 8.
When ¬rst says ˜gimme™, our opening strategy this time will be to type

hide(p3 = (0,0){right}..{up}(1,1)) p3

so that we have a new path to play with. Now the fun begins:

You type And the result is
directiontime right of p3 0
directiontime up of p3 1
directiontime down of p3 -1
directiontime (1,1) of p3 0.5
directiontime left of reverse p3 1
direction directiontime (1,2) of p3 of p3 (0.23126,0.46251)
directiontime right of subpath(epsilon,1) of p3 0
directiontime right of subpath(2epsilon,1)of p3 -1
directiontime (1,1) of subpath(epsilon,1) of p3 0.49998
direction epsilon of p3 (0.55226,0)
direction 2epsilon of p3 (0.55229,0.00003)
directiontime dir 30 of p3 0.32925
angle direction 0.32925 of p3 29.99849
angle direction 0.32925+epsilon of p3 30.00081
directionpoint up of p3 (1,1)

Note that directiontime yields ’1 if the speci¬ed direction doesn™t occur. At time
epsilon , path p3 is still traveling right, but at time 2epsilon it has begun to turn
upward. The ˜directionpoint™ operation is analogous to directiontime, but it gives the
point on the path rather than the time of arrival.

You type And the result is
directiontime up of fullcircle 0
directiontime left of fullcircle 2
directiontime right of fullcircle 6
directiontime (-1,1) of fullcircle 1
directiontime (epsilon,infinity) of fullcircle 8
136 Chapter 14: Paths


directiontime right of unitsquare 0 unitsquare
cusp
directiontime up of unitsquare 1 strange
turning numbers
directiontime (1,1) of unitsquare 1
tension
directiontime (-1,1) of unitsquare 2 posttension
pretension
If a path travels in a given direction more than once, directiontime reports only the ¬rst intersection
halfcircle
time. The unitsquare path has sharp turns at the corners; directiontime considers that
all directions between the incoming and outgoing ones are instantaneously present.
It™s possible to construct pathological paths in which unusual things happen.
For example, the path p = (0, 0) . . controls (1, 1) and (0, 1) . . (1, 0) has a
“cusp” at time 0.5, when it comes to a dead stop and turns around. (If you ask
for ˜direction 0.5 of p™, the answer is zero, while direction 0.5 ’ of p is (0, 2 ) and
direction 0.5 + of p is (0, ’2 ).) The directiontime operation assumes that all possible
directions actually occur when a path comes to a standstill, hence ˜directiontime right
of p™ will be 0.5 in this case even though it might be argued that p never turns to the
right. Paths with cusps are numerically unstable, and they might become “strange”
after transformations are applied, because rounding errors might change their turning
numbers. The path p in this example has control points that correspond to tensions of
only 0.28 with respect to the initial and ¬nal directions; since insists that
tensions be at least 0.75, this anomalous path could never have arisen if the control
points hadn™t been given explicitly.
EXERCISE 14.15
Write macros called posttension and pretension that determine the e¬ective
tensions of a path™s control points at integer times t. For example, ˜pretension 1
of (z0 . . tension ± and β . . z1 )™ should be β (approximately). Test your macro by
computing posttension 0 of ((0, 0){right } . . . {up }(1, 10)).
We have now discussed almost all of the things that can do
with paths; but there™s one more important operation to consider, namely
intersection. Given two paths p and q, you can write
p intersectiontimes q
and the result will be a pair of times (t, u) such that point t of p ≈ point u of q. For
example, using the expr routine,
You type And the result is
unitsquare intersectiontimes fullcircle (0.50002,0)
unitsquare intersectiontimes fullcircle rotated 90 (0.50002,6)
reverse unitsquare intersectiontimes fullcircle (0.50002,2)
fullcircle intersectiontimes unitsquare (0,0.50002)
halfcircle rotated 45 intersectiontimes unitsquare (1,3.5)
halfcircle rotated 89 intersectiontimes unitsquare (0.02196,3.5)
halfcircle rotated 90 intersectiontimes unitsquare (0,3.50002)
halfcircle rotated 91 intersectiontimes unitsquare (-1,-1)
halfcircle rotated 45 intersectiontimes fullcircle (0,1)
fullcircle intersectiontimes (-0.5,0) (4,0)
Chapter 14: Paths 137


unitsquare intersectionpoint fullcircle (0.5,0) intersectionpoint
tertiary level
reverse unitsquare intersectionpoint fullcircle (0,0.5) precedence
Quick
Notice that the result is (’1, ’1) if the paths don™t intersect. The last two examples il- shu¬„ed binary
Journal of Algorithms
lustrate the ˜intersectionpoint™ operator, which yields the common point of intersection.
logo
Both intersectiontimes and intersectionpoint apply at the tertiary level of precedence, Knuth, J C
hence parentheses were not needed in these examples.

EXERCISE 14.16
J. H. Quick (a student) wanted to construct a path r that started on some
previously de¬ned path p and proceeded up to the point where it touched another
path q, after which r was supposed to continue on path q. So he wrote

path r; numeric t, u; (t, u) = p intersectiontimes q;
r = subpath (0, t) of p & subpath (u, in¬nity ) of q;

but it didn™t work. Why not?

If the paths intersect more than once, has a somewhat peculiar
way of deciding what times (t, u) should be reported by ˜p intersectiontimes q™.
Suppose p has length m and q has length n. (Paths of length 0 are ¬rst changed into
motionless paths of length 1.) proceeds to examine subpath (k, k + 1) of p
versus subpath (l, l + 1) of q, for k = 0, . . . , m ’ 1 and l = 0, . . . , n ’ 1, with l varying
most rapidly. This reduces the general problem to the special case of paths of length 1,
and the times (t, u) for the ¬rst such intersection found are added to (k, l). But within
paths of length 1 the search for intersection times is somewhat di¬erent: Instead of
reporting the “lexicographically smallest” pair (t, u) that corresponds to an intersection,
¬nds the (t, u) whose “shu¬„ed binary” representation (.t1 u1 t2 u2 . . . )2 is
minimum, where (.t1 t2 . . . )2 and (.u1 u2 . . . )2 are the radix-2 representations of t and u.

EXERCISE 14.17
(A mathematical puzzle.) The path p = (0, 0) . . controls (2, 2) and (0, 1) . .
(1, 0) loops on itself, so there are times t < u such that point t of p ≈ point u of p.
Devise a simple way to compute (t, u) in a program, without using the
subpath operation.

Let™s conclude this chapter by applying what we™ve learned about paths to a
real-life example. The Journal of Algorithms has been published since 1980
by Academic Press, and its cover page carries the following logo, which was designed
by J. C. Knuth to blend with the style of type used elsewhere on that page:




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




A program to produce this logo will make it possible for the editors of the
journal to use it on letterheads in their correspondence. Here is one way to do the job,
138 Chapter 14: Paths


without needing to erase anything: superellipse
whatever
beginchar ("A", 29mm #, 25mm #, 0); thick # := 2mm #; thin # := 5/4mm #; rotatedaround
1
re¬‚ectedabout
de¬ne whole blacker pixels(thick , thin );
2
forsu¬xes
forsu¬xes $ = a, b, c: transform $;
3 penstroke
range
forsu¬xes e = l, r: path $e, $ e; numeric t$[ ]e; endfor endfor
4
thru
penpos1 (thick , 0); penpos2 (thick , 90); penpos3 (thick , 180); penpos4 (thick , 270);
5
penpos5 (thick , 0); penpos6 (thick , 90); penpos7 (thick , 180); penpos8 (thick , 270);
6
x2 = x4 = x6 = x8 = .5[x5 , x7 ] = .5w; x1r = w; x3r = 0; x5 ’ x7 = y6 ’ y8 ;
7
y1 = y3 = y5 = y7 = .5[y6 , y8 ] = .5h; y2r = h; y4r = 0; y6r = .75h;
8
forsu¬xes e = l, r: a.e = b e = c e = superellipse (z1e , z2e , z3e , z4e , .75);
9
a e = b.e = c.e = superellipse (z5e , z6e , z7e , z8e , .72); endfor
10
penposa1 (thin , 0); penposa5 (whatever , ’90); penposa9 (thin , 180);
11
xa1l ’ xa9l = 1/3(x5l ’ x7l ); xa5 = .5w; ya1 = ya9 ; ya5r = 4/7h;
12
xa3l = xa1l ; xa3r = xa1r ; xa4r = 1/6[xa3r , x1l ]; x0 = .5w; y0 = .52h;
13
xa6l + xa4l = xa6r + xa4r = xa7l + xa3l = xa7r + xa3r = xa9 + xa1 = w;
14
ya3r = ya4r = ya6r = ya7r = .2[y2l , y0 ]; ya3l = ya4l = ya6l = ya7l = ya3r ’ thin ;
15
za4l = za4r + (thin , 0) rotated(angle(za4r ’ za5r ) + 90)
16
+ whatever — (za4r ’ za5r ); za4l ’ za5l = whatever — (za4r ’ za5r );
17
z = a.r intersectionpoint (z0 - - (w, 0)); ya1 ’ ya5 = length(z ’ z0 );
18
b = identity shifted (0, y0 ’ ya1 ) rotatedaround(z0 , 90 ’ angle(z0 ’ (w, 0)));
19
c = b re¬‚ectedabout (z2 , z4 );
20
for n = 1, 3, 4, 5, 6, 7, 9: forsu¬xes e = l, , r: forsu¬xes $ = b, c:
21
z$[n]e = za[n]e transformed $; endfor endfor endfor
22
forsu¬xes e = l, r: forsu¬xes $ = a, b, c:
23
z$2e = $r intersectionpoint (z$1e - - z$3e );
24
z$8e = $r intersectionpoint (z$9e - - z$7e );
25
t$1e = xpart($e intersectiontimes (z$1l - - z$3l ));
26
t$9e = xpart($e intersectiontimes (z$9l - - z$7l ));
27
t$4e = xpart($ e intersectiontimes (z$5r - - z$4l ));
28
t$6e = xpart($ e intersectiontimes (z$5r - - z$6l )); endfor endfor
29
penstroke subpath(ta9e , tb6e ) of a.e;
30
penstroke subpath(tb4e , tc4e ) of b e;
31
penstroke subpath(tc6e , ta1e + 8) of c e;
32
penstroke subpath(ta6e , tb9e ) of a e;
33
penstroke subpath(tb1e , tc1e ) of b.e;
34
penstroke subpath(tc9e , ta4e + 8) of c.e;
35
forsu¬xes $ = a, b, c: penlabels($1, $2, $3, $4, $5, $6, $7, $8, $9);
36
penstroke z$2e - - z$3e - - z$4e - - z$5e - - z$6e - - z$7e - - z$8e ; endfor
37
penlabels(range 0 thru 8); endchar;
38

Lines 5“10 of this program de¬ne the main superellipses of the ¬gure. The outer
superellipse is eventually drawn as three separate strokes in lines 30“32, and the inner
one is drawn as three strokes in lines 33“35. The rest of the ¬gure consists of three
arrows, whose point labels are prefaced by the respective labels a, b, c. Lines 11“18
de¬ne the ˜a™ arrow; then lines 19“22 transform these points into the ˜b™ and ˜c™ arrows,
anticipating some of the things we shall discuss in Chapter 15. Thirty-six intersections
between arrows and superellipses are computed in lines 23“29, and the arrows are ¬nally
drawn by the penstrokes speci¬ed in lines 36“37.
Chapter 14: Paths 139


El Palo Alto
FONT
Giotto
RUSKIN




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




The route is indicated by dots,
the days™ journeys are expressed by numbers,
and letters are used to locate notable places and sites.
. . . We arrived at the Arroyo de San Francisco,
beside which stream is the redwood tree I spoke of yesterday;
I measured its height with the Graphometer
and reckoned it to be ¬fty yards high, more or less.
” FRAY PEDRO FONT, Diary (1776)

The practical teaching of the masters of Art was summed by the O of Giotto.
” JOHN RUSKIN, The Cestus of Aglaia (1865)
(page 140)




15
Transformations
Chapter 15: Transformations 141


Points, paths, pens, and pictures can be shifted, scaled, rotated, and revamped transformations
shifted
in a variety of ways. Our aim in this chapter will be to learn all about the built- scaled
in metamorphoses of , because they can make programs simpler and xscaled
yscaled
more versatile. slanted
The basic transformations have already appeared in many examples, but rotated
zscaled
let™s start by reviewing them here: rotatedaround
re¬‚ectedabout
(x, y) shifted (a, b) = (x + a, y + b); transformed
transform
(x, y) scaled s = (sx, sy);
equations
(x, y) xscaled s = (sx, y); identity
(x, y) yscaled s = (x, sy);
(x, y) slanted s = (x + sy, y);
= (x cos θ ’ y sin θ, x sin θ + y cos θ);
(x, y) rotated θ
= (xu ’ yv, xv + yu).
(x, y) zscaled (u, v)
One of the nice things about is that you don™t have to remem-
ber the sine-and-cosine formulas of trigonometry; you just have to know that
˜(x, y) rotated θ™ means ˜the vector (x, y) rotated θ degrees counterclockwise
around (0, 0)™, and the computer does all the necessary calculations by itself.
The operation of zscaling may look a bit strange, but it is simply a combination
of rotating by angle (u, v) and scaling by length (u, v).
Plain provides two more transformations that are commonly
needed: You can say ˜(x, y) rotatedaround (z0 , θ)™ if you want to rotate around
point z0 instead of point (0, 0). And you can say ˜(x, y) re¬‚ectedabout (z1 , z2 )™
if you want to ¬nd the point directly opposite (x, y) on the other side of the
straight line that runs through z1 and z2 .
All of these operations are special manifestations of a single glorious
maneuver that can be written in the general form
(x, y) transformed t.
Here t is a variable (or primary expression) of type transform; it stands for any
desired sequence of shiftings, scalings, slantings, etc., all in one fell swoop.
You can give equations between transforms, just as you can give equa-
tions between other types of things in programs. Thus, for example,
you might say
transform t[ ]; t2 = t1 shifted (2, 2) rotated 30;
then an expression like ˜(x, y) transformed t1 shifted (2, 2) rotated 30™ can be
abbreviated to ˜(x, y) transformed t2 ™, which is simpler and faster.
There™s a special transform variable called identity with the amazing
property that
(x, y) transformed identity = (x, y)
for all x and y. You might think that identity is useless, since it does nothing, but
in fact it™s a natural starting point for building other transforms. For example,
142 Chapter 15: Transformations


line 19 of the program at the end of the previous chapter says show
xpart
b = identity shifted (0, y0 ’ ya1 ) rotatedaround(z0 , theta ); ypart
xypart
yxpart
this de¬nes the transform variable b to be a compound transformation that is yypart
xxpart
used on lines 21 and 22 to construct the lower left arrow as a shifted and rotated
copy of the upper arrow, in the character being drawn.
A transform variable t represents six numbers (tx , ty , txx , txy , tyx , tyy ), in
much the same way as a pair variable represents two numbers (x, y). The
general transformation ˜(x, y) transformed t™ is simply an abbreviation for

(tx + x txx + y txy , ty + x tyx + y tyy );

thus, for example, ˜txy ™ appears in the xpart of the transform as the coe¬cient of y. If
you say ˜show t™ when t is a completely unknown transform, the computer will type

>> (xpart t,ypart t,xxpart t,xypart t,yxpart t,yypart t)

just as it would type ˜>> (xpart u,ypart u)™ for a completely unknown variable u
of type pair. You can access individual components of a transform by referring to
˜xpart t™, ˜ypart t™, ˜xxpart t™, etc.

Once again, we can learn best by computer experiments with the expr ¬le
(cf. Chapter 8); this time the idea is to play with transforms:

You type And the result is
identity (0,0,1,0,0,1)
identity shifted (a,b) (a,b,1,0,0,1)
identity scaled s (0,0,s,0,0,s)
identity xscaled s (0,0,s,0,0,1)
identity yscaled s (0,0,1,0,0,s)
identity slanted s (0,0,1,s,0,1)
identity rotated 90 (0,0,0,-1,1,0)
identity rotated 30 (0,0,0.86603,-0.5,0.5,0.86603)
identity rotatedaround ((2,3),90) (5,1,0,-1,1,0)
(x,y) rotatedaround ((2,3),90) (-y+5,x+1)
(x,y) reflectedabout ((0,0),(0,1)) (-x,y)
(x,y) reflectedabout ((0,0),(1,1)) (y,x)
(x,y) reflectedabout ((5,0),(0,10)) (-0.8y-0.6x+8,0.6y-0.8x+4)

EXERCISE 15.1
Guess the result of ˜(x,y) reflectedabout ((0,0),(1,0))™.

EXERCISE 15.2
What transform takes (x, y) into (’x, ’y)?

EXERCISE 15.3
(’(x, y)) transformed t = ’((x, y) transformed t).
True or false:
Chapter 15: Transformations 143


In order to have some transform variables to work with, it™s necessary to ˜hide™ hide
inverse
some declarations and commands before giving the next exprs:
known
unknown
You type And the result is left-handed dangerous bend
hide(transform t[]) t1 (xpart t1,ypart t1,xxpart...)
hide(t1=identity zscaled(1,2)) t1 (0,0,1,-2,2,1)
hide(t2=t1 shifted (1,2)) t2 (1,2,1,-2,2,1)
t2 xscaled s (s,2,s,-2s,2,1)
unknown t2 false
transform t2 true
t1=t2 false
t1<t2 true
inverse t2 (-1,0,0.2,0.4,-0.4,0.2)
inverse t2 transformed t2 (0,0,0.99998,0,0,0.99998)
hide(t3 transformed t2=identity) t3 (-1,0,0.2,0.4,-0.4,0.2)
The inverse function ¬nds the transform that undoes the work of another; the equation
that de¬nes t3 above shows how to calculate an inverse indirectly, without using inverse .
Like numeric expressions and pair expressions, transform expressions can be
either “known” or “unknown” at any given point in a program. (If any
component of a transform is unknown, the whole transform is regarded as unknown.)
You are always allowed to use the constructions
known transformed known
unknown transformed known
known transformed unknown
but will balk at ˜ unknown transformed unknown ™. This is not the most
lenient rule that could have been implemented, but it does have the virtue of being
easily remembered.
EXERCISE 15.4
If z1 and z2 are unknown pairs, you can™t say ˜z1 shifted z2 ™, because ˜shifted z2 ™
is an unknown transform. What can you legally say instead?
EXERCISE 15.5
Suppose dbend is a picture variable that contains a normal dangerous bend
sign, as in the “reverse-video” example of Chapter 13. Explain how to transform it
into the left-handed dangerous bend that heads this paragraph.
The next three lines illustrate the fact that you can specify a transform com-
pletely by specifying the images of three points:
You type And the result is
hide((0,0)transformed t4=(1,2)) t4 (1,2,xxpart t4,xypart t4,...)
hide((1,0)transformed t4=(4,5)) t4 (1,2,3,xypart t4,3,yypart t4)
hide((1,4)transformed t4=(0,0)) t4 (1,2,3,-1,3,-1.25)
The points at which the transform is given shouldn™t all lie on a straight line.
144 Chapter 15: Transformations


Now let™s use transformation to make a little ornament, based on a ˜ ™ shape ornament
rotatedaround
replicated four times:
addto
picture




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




The following program merits careful study:

beginchar ("4", 11pt #, 11pt #, 0);
1
pickup pencircle scaled 3/4pt yscaled 1/3 rotated 30;
2
transform t;
3
t = identity rotatedaround((.5w, .5h), ’90);
4
x2 = .35w; x3 = .6w;
5
y2 = .1h; top y3 = .4h;
6
path p; p = z2 {right } . . . {up }z3 ;
7
top z1 = point .5 of p transformed t;
8
draw z1 . . . p;
9
addto currentpicture also currentpicture transformed t;
10
addto currentpicture also currentpicture transformed (t transformed t);
11
labels(1, 2, 3); endchar;
12

Lines 3 and 4 compute the transform that moves each ˜ ™ to its clockwise neighbor.
Lines 5“7 compute the right half of the ˜ ™. Line 8 is the most interesting: It puts
point z1 on the rotated path. Line 9 draws the ˜ ™, line 10 changes it into two, and
line 11 changes two into four. The parentheses on line 11 could have been omitted, but
it is much faster to transform a transform than to transform a picture.

will transform a picture expression only when txx , txy , tyx , and tyy
are integers and either txy = tyx = 0 or txx = tyy = 0; furthermore, the values
of tx and ty are rounded to the nearest integers. Otherwise the transformation would
not take pixel boundaries into pixel boundaries.

EXERCISE 15.6
Explain how to rotate the ornament by 45—¦ .
Chapter 15: Transformations 145


Plain maintains a special variable called currenttransform , currenttransform
¬ll
behind the scenes. Every ¬ll and draw command is a¬ected by this variable; draw
for example, the statement ˜¬ll p™ actually ¬lls the interior of the path nonsquare pixels
aspect ratio
p transformed currenttransform pickup
DICKENS
TWAIN
instead of p itself. We haven™t mentioned this before, because currenttransform
is usually equal to identity ; but nonstandard settings of currenttransform can be
used for special e¬ects that are occasionally desired. For example, it™s possible
to change ˜ ™ to ˜ ™ by simply saying
currenttransform := identity slanted 1/4
and executing the programs of logo.mf that are described in Chapter 11; no
other changes to those programs are necessary.
It™s worth noting that the pen nib used to draw ˜ ™ was not
slanted when currenttransform was changed; only the “tracks” of the pen, the
paths in draw commands, were modi¬ed. Thus the slanted image was not simply
obtained by slanting the unslanted image.
When fonts are being made for devices with nonsquare pixels, plain -
will set currenttransform to ˜identity yscaled aspect ratio ™, and pickup
will similarly yscale the pen nibs that are used for drawing. In this case the slanted
˜ ™ letters should be drawn with
currenttransform := identity slanted 1/4 yscaled aspect ratio .
EXERCISE 15.7
Our program for ˜ ™ doesn™t work when pixels aren™t square. Fix it so that
it handles a general aspect ratio .




Change begets change. Nothing propagates so fast.
” CHARLES DICKENS, Martin Chuzzlewit (1843)

There are some that never know how to change.
” MARK TWAIN, Joan of Arc (1896)
(page 146)




16
Calligraphic
E¬ects
Chapter 16: Calligraphic E¬ects 147


Pens were introduced in Chapter 4, and we ought to make a systematic study of Pens
pen
what can do with them before we spill any more ink. The purpose pickup
of this chapter will be to explore the uses of “¬xed” pen nibs”i.e., variables pen expression
currentpen
and expressions of type pen”rather than to consider the creation of shapes by top
means of outlines or penstrokes. bot
lft
When you say ˜pickup pen expression ™, the macros of plain - rt
do several things for you: They create a representation of the speci¬ed draw
drawdot
pen nib, and assign it to a pen variable called currentpen ; then they store away ¬lldraw
information about the top, bottom, left, and right extents of that pen, for use savepen
clear pen memory
in top , bot , lft , and rt operations. A draw or drawdot or ¬lldraw command penrazor
will make use of currentpen to modify the current picture. pensquare
convex polygon
You can also say ˜pickup numeric expression ™; in this case the nu- makepen
meric expression designates the code number of a previously picked-up pen turning number
that was saved by ˜savepen™. For example, the logo.mf ¬le in Chapter 11
begins by picking up the pen that™s used to draw ˜ ™, then it says
˜logo pen := savepen™. Every character program later in that ¬le begins with
the command ˜pickup logo pen ™, which is a fast operation because it doesn™t
require the generation of a new pen representation inside the computer.
Caution: Every time you use savepen, it produces a new integer value and
stashes away another pen for later use. If you keep doing this, ™s
memory will become cluttered with the representations of pens that you may never
need again. The command ˜clear pen memory™ discards all previously saved pens
and lets start afresh.

But what is a pen expression ? Good question. So far in this book, almost
everything that we™ve picked up was a pencircle followed by some sequence of
transformations; for example, the logo pen of Chapter 11 was ˜pencircle xscaled px
yscaled py ™. Chapter 13 also made brief mention of another kind of pen, when it said

pickup penrazor scaled 10;

this command picks up an in¬nitely thin pen that runs from point (’5, 0) to point
(5, 0) with respect to its center. Later in this chapter we shall make use of pens like

pensquare xscaled 30 yscaled 3 rotated 30;

this pen has a rectangular boundary measuring 30 pixels — 3 pixels, inclined at an
angle of 30—¦ to the baseline.

You can de¬ne pens of any convex polygonal shape by saying ˜makepen p™,
where p is a cyclic path. It turns out that looks only at the key
points of p, not the control points, so we may as well assume that p has the form
z0 - - z1 - - etc. - - cycle. This path must have the property that it turns left at every
key point (i.e., zk+1 must lie to the left of the line from zk’1 to zk , for all k), unless the
cycle contains fewer than three key points; furthermore the path must have a turning
number of 1 (i.e., it must not make more than one counterclockwise loop). Plain -
™s penrazor stands for ˜makepen ((’.5, 0) - - (.5, 0) - - cycle)™, and pensquare
is an abbreviation for ˜makepen (unitsquare shifted ’(.5, .5))™. But pencircle is not
148 Chapter 16: Calligraphic E¬ects


de¬ned via makepen; it is a primitive operation of . It represents a true circle
circle of diameter 1, passing through the points (±.5, 0) and (0, ±.5). pen primary
(
)
The complete syntax for pen expressions is rather short, because you can™t nullpen
really do all that much with pens. But it also contains a surprise: future pen primary
pencircle
pen primary ’’ pen variable makepen
pen secondary
| ( pen expression ) future pen secondary
| nullpen pen tertiary
pen expression
future pen primary ’’ pencircle nullpen
| makepen path primary ¬lldraw
pen secondary ’’ pen primary ¬ll
beginchar
future pen secondary ’’ future pen primary future pen
| future pen secondary transformer diamond-shaped nib
| pen secondary transformer
pen tertiary ’’ pen secondary
| future pen secondary
pen expression ’’ pen tertiary

The constant ˜nullpen™ is just the single point (0, 0), which is invisible”unless you
use it in ¬lldraw, which then reduces to ¬ll. (A beginchar command initializes
currentpen to nullpen, in order to reduce potentially dangerous dependencies between
the programs for di¬erent characters.) The surprise in these rules is the notion of a
“future pen,” which stands for a path or an ellipse that has not yet been converted into
™s internal representation of a true pen. The conversion process is rather
complicated, so procrastinates until being sure that no more transforma-
tions are going to be made. A true pen is formed at the tertiary level, when future
pens are no longer permitted in the syntax.

The distinction between pens and future pens would make no di¬erence to a
user, except for another surprising fact: All of ™s pens are convex
polygons, even the pens that are made from pencircle and its variants! Thus, for
example, the pen you get from an untransformed pencircle is identical to the pen you
get by specifying the diamond-shaped nib

makepen ((.5, 0) - - (0, .5) - - (’.5, 0) - - (0, ’.5) - - cycle).

And the pens you get from ˜pencircle scaled 20™ and ˜pencircle xscaled 30 yscaled 20™
are polygons with 32 and 40 sides, respectively:




(Figure 16a&b will be inserted here; too bad you can™t see it now.)
Chapter 16: Calligraphic E¬ects 149


The vertices of the polygons, shown as heavy dots in this illustration, all have “half- digitization
Hobby
integer” coordinates; i.e., each coordinate is either an integer or an integer plus 1/2.
Every polygon that comes from a pencircle is symmetric under 180—¦ rotation; further-
more, there will be re¬‚ective left/right and top/bottom symmetry if the future pen is
a circle, or if it™s an ellipse that has not been rotated.

This conversion to polygons explains why future pens must, in general, be
distinguished from ordinary ones. For example, the extra parentheses in
˜(pencircle xscaled 30) yscaled 20™ will yield a result quite di¬erent from the elliptical
polygon just illustrated. The parentheses force conversion of ˜pencircle xscaled 30™
from future pen to pen, and this polygon turns out to be

(12.5, ’0.5) - - (15, 0) - - (12.5, 0.5)
- - (’12.5, 0.5) - - (’15, 0) - - (’12.5, ’0.5) - - cycle,

an approximation to a 30 — 1 ellipse. Then yscaling by 20 yields




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




Why does work with polygonal approximations to circles, instead
of true circles? That™s another good question. The main reason is that suitably
chosen polygons give better results than the real thing, when digitization is taken into
account. For example, suppose we want to draw a straight line of slope 1/2 that™s
exactly one pixel thick, from (0, y) to (200, y + 100). The image of a perfectly circular
along this line has outlines that run from (0, y ± ±) to
pen of diameter 1 that travels √
(200, y + 100 ± ±), where ± = 5/4 ≈ 0.559. If we digitize these outlines and ¬ll the
region between them, we ¬nd that for some values of y (e.g., y = 0.1) the result is
...
a repeating pixel pattern like ˜ . . . ™; but for other values of y (e.g., y = 0.3)
...
the repeating pattern of pixels is 50 percent darker: ˜ . . . ™. Similarly, some
diagonal lines of slope 1 digitize to be twice as dark as others, when a truly circular
pen is considered. But the diamond-shaped nib that uses for a pencircle
of diameter 1 does not have this defect; all straight lines of the same slope will digitize
to lines of uniform darkness. Moreover, curved lines drawn with the diamond nib
always yield one pixel per column when they move more-or-less horizontally (with slopes
between +1 and ’1), and they always yield one pixel per row when they move vertically.
By contrast, the outlines of curves drawn with circular pens produce occasional “blots.”
Circles and ellipses of all diameters can pro¬tably be replaced by polygons whose sub-
pixel corrections to the ideal shape will produce better digitizations; does
this in accordance with the interesting theory developed by John D. Hobby in his Ph.D.
dissertation (Stanford University, 1985).
150 Chapter 16: Calligraphic E¬ects


It™s much easier to compute the outlines of a polygonal pen that follows a peno¬set
makepath
given curve than to ¬gure out the corresponding outlines of a truly circular
¬llin
pen; thus polygons win over circles with respect to both quality and speed. When a penrazor
curve is traveling in a direction between the edge vectors zk+1 ’ zk and zk ’ zk’1 of a endpoints
drawdot
polygonal pen, the curve™s outline will be o¬set from its center by zk . If you want ¬ne draw
control over this curve-drawing process, provides the primitive operation cuto¬
currentpen
˜peno¬set w of p™, where w is a vector and p is a pen. If w = (0, 0), the result is (0, 0);
if the direction of w lies strictly between zk+1 ’ zk and zk ’ zk’1 , the result is zk ; and
if w has the same direction as zk+1 ’ zk for some k, the result is either zk or zk+1 ,
whichever ¬nds most convenient to compute.
EXERCISE 16.1
Explain how to use peno¬set to ¬nd the point or points at the “top” of a pen
(i.e., the point or points with largest y coordinate).
The primitive operation ˜makepath p™, where p is a (polygonal) pen whose
vertices are z0 , z1 , . . . , zn’1 , produces the path ˜z0 . . controls z0 and z1 . . z1 . .
etc. . . zn’1 . . controls zn’1 and z0 . . cycle™, which is one of the paths that might have
generated p. This gives access to all the o¬sets of a pen.
When a pencircle is transformed by any of the operations in Chapter 15, it
changes into an ellipse of some sort, since all of ™s transformations
preserve ellipse-hood. The diameter of the ellipse in each direction θ is decreased by
2 min(| sin θ|, | cos θ|) times the current value of ¬llin , before converting to a polygon;
this helps to compensate for the variation in thickness of diagonal strokes with respect
uses ¬llin
to horizontal or vertical strokes, on certain output devices. (
only when creating polygons from ellipses, but users can of course refer to ¬llin within
their own routines for drawing strokes.) The ¬nal polygon will never be perfectly ¬‚at
like penrazor, even if you say ˜xscaled 0™ and/or ˜yscaled 0™; its center will always be
surrounded at least by the basic diamond nib that corresponds to a circle of diameter 1.
EXERCISE 16.2
Run on the expr ¬le of Chapter 8 and look at what is typed
when you ask for ˜pencircle™ and ˜pencircle scaled 1.1™. (The ¬rst will exhibit the
diamond nib, while the second will show a polygon that™s equivalent to pensquare.)
Continue experimenting until you ¬nd the “threshold” diameter where
decides to switch between these two polygons.
™s polygonal pens work well for drawing lines and curves, but this
pleasant fact has an unpleasant corollary: They do not always digitize well
at the endpoints, where curves start and stop. The reason for this is explored further
in Chapter 24; polygon vertices that give nice uniform stroke widths might also be
“ambiguous” points that cause di¬culties when we consider rounding to the raster.
Therefore a special drawdot routine is provided for drawing one-point paths. It is
sometimes advantageous to apply drawdot to the ¬rst and last points of a path p,
after having said ˜draw p™; this can fatten up the endpoints slightly, making them look
more consistent with each other.
Plain also provides two routines that can be used to clean up
endpoints in a di¬erent way: The command ˜cuto¬ (z, θ)™ removes half of the
currentpen image at point z, namely all points of the pen that lie in directions between
Chapter 16: Calligraphic E¬ects 151


(θ ’ 90)—¦ and (θ + 90)—¦ from the center point. And the command ˜cutdraw p™ is an cutdraw
T
abbreviation for the following three commands:
addto
cull
draw p; cuto¬ (point 0 of p, 180 + angle direction 0 of p); picture
cuto¬ (point in¬nity of p, angle direction in¬nity of p). doublepath
pen lft
lft
The e¬ect is to draw a curve whose ends are clipped perpendicular to the starting and
rt
ending directions. For example, the command top
bot
cutdraw z4 . . controls z1 and z2 . . z6

produces the following curve, which invites comparison with the corresponding uncut
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.

<<

. 5
( 13)



>>