<< . .

. 16
( : 45)



. . >>

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

<< . .

. 16
( : 45)



. . >>