PicScheme, or the Scheme code in picscheme.tex
,
provides a set of picture-making primitives that is
similar in spirit to LaTeX’s {picture}
environment [4, ch. 7], except that the
commands are in Scheme and can be combined using full
Scheme.
You can specify picture objects such as paths and texts and position them in a composite picture object called a picture. In general, a picture specification looks as follows:
\eval{
... ;Scheme code, enhanced with ;the picture-drawing primitives ...
}
The picture produced is a box, which means it
will be typeset as a giant letter might be.
We can use the usual TeX approaches to centering and
displaying boxes, eg, \centerline
.
The picture description makes use of a unit
length, which is stored in the variable *unit‑length*
and is by default 1 bp (big point), which is 1/72 of an inch.
Regardless of the *unit‑length*
, PicScheme
internally manipulates lengths as multiples of big
points, and thus the default initial value of
of *unit‑length*
is the number 1.
In order to allow the resetting of *unit‑length*
easily,
PicScheme provides the procedure dimen
, which converts
lengths in other units to scaled points. For example,
(dimen 1 'in)
returns the bp equivalent of 1 inch, which is 72.
dimen
’s second argument is thus a symbol naming the
unit converted from, and this can be one of pt
(point), pc
(pica), in
(inch), bp
(big
point), cm
(centimeter), mm
(millimeter),
dd
(didot point), cc
(cicero), and sp
(scaled
point) [3, ch. 10], [2, ch. 11].
(The word dimen is merely standard TeX jargon for
(non-springy) length — the only physical dimension
that is relevant to typesetting.)
We can thus
set *unit‑length*
to be 42 mm by
(set! *unit-length* (* 42 (/ 25.4) 72))
where we make use of the equivalences 1 mm = 1/25.4 inch, 1 inch = 72 bp, or, we could say
(set! *unit-length* (dimen 42 'mm))
Use whichever is easier for you. Changing
*unit‑length*
is a convenient way to scale a
picture without rewriting its innards.
An important length quantity is
the thickness of (the lead of) the imaginary pencil used to
draw a PicScheme picture. By default this is 0.5 bp.
You may reset it using the pencil‑width
procedure.
Thus
(pencil-width 1/72)
sets the pencil width to 1/72 of whatever the
prevailing *unit‑length*
is. (Setting pencil width is
probably the only occasion where it may be more
convenient to specify a true length in bp rather than a
multiple of the prevailing *unit‑length*
. To set
it to 0.5 bp, you can use
(pencil‑width (/ (dimen .5 'bp) *unit‑length*))
,
which does the correct thing regardless of the value of
*unit‑length*
.)
The 0-argument procedures
thin‑lines
and thick‑lines
can be used to toggle between
the default pencil width and a value that is twice the
default width.
(thin‑lines)
sets pencil
thickness to the default .5 bp, while
(thick‑lines)
sets it to 1 bp.
A picture is introduced by a call to the
picture
procedure:
(picture th)
th
is a thunk (zero-argument procedure) that
will contain commands to populate the picture.
For example,
\centerline{
\eval{ (set! *unit‑length* (dimen 1 'cm)) (picture (lambda () (label (point 0 0) "nothing special"))) }
}
produces
The only “picture” here is a text object, created by
the procedure label
, but it
nevertheless serves to illustrate the use of
coordinates. The procedure point
makes a point
from its x- and y-coordinates. PicScheme represents
fully determined points as complex numbers, with the x-coordinate
as the real part and the y-coordinate as the imaginary
part, so (point 2 3)
can be more succinctly entered
as 2+3i
. In particular, (point 0 0)
is simply
0
. In the following, we will use the shorter
complex-number notation when feasible.
The first argument to label
is
the
point at which to place
the second argument, which can be any valid TeX
fragment.
Eg1
\eval{ (picture (lambda () (label ‑3 "West") (label +3i "North") (label 3 "East") (label ‑3i "South"))) }
produces
Note that the center of the text coincides with the
point supplied.
label
can take a third argument that can place
the text at a different relation to the chosen point.
In the following example, the label
’s point is
the center of the “cross hairs”, and we use the
position indicators ulft
and lrt
to put the text "UPPER LEFT"
on the upper left of the
point,
and the text "LOWER RIGHT"
on its lower right.
The various position
possibilities are lft
, ulft
, top
,
urt
, rt
, lrt
, bot
, llft
.
\eval{ (picture (lambda () (draw (path ‑2.5i '‑‑ +2.5i)) (draw (path ‑2.5 '‑‑ 2.5)) ; (label 0 "UPPER LEFT" 'ulft) (label 0 "LOWER RIGHT" 'lrt))) }
produces
The procedure path
returns a path
passing through a sequence of points.
The form of the path between successive points is
governed by the symbols ‑‑
and **
, which produce straight-line
and curved segments respectively. Additional arguments such
as direction
, controls
(with and
), and cycle
can further alter the path.
The procedure
draw
draws its argument path.
\eval{ (set! *unit‑length* (dimen 1 'cm)) (picture (lambda () (draw (path 0 '‑‑ 10 '‑‑ 10+10i '‑‑ +10i '‑‑ 'cycle)) (let ((lft‑control‑point 3+12i) (rt‑control‑point 7+12i)) (draw (path 2+2i '** 'controls lft‑control‑point '** 7+2i)) (draw (path 3+2i '** 'controls rt‑control‑point '** 8+2i))) (label 9.8+0.2i "{\\it 020203120702, 030207120802}" 'ulft))) }
produces
'controls z1
uses z1
as a control point (instead of having the path pass through it) for
the enclosing points.
For instance, a Bezier curve with the three points
z1
, zc
, z2
connects z1
and z2
using
zc
as a control point, and can be represented as
(path z1 '** 'controls zc '** z2)
'controls z1 'and z2
uses both z1
and z2
as control points
for the enclosing points.
cycle
, if it occurs at all, should be the
last argument, and returns a closed path. direction
governs the direction of the part of the path at
the adjacent point (which could be directly to the left or to the right).
Here is a grid:
\eval{ (set! *unit‑length* (dimen .5 'cm)) (picture (lambda () ;draw vertical lines (let loop ((x 0)) (when (<= x 20) ((if (= (modulo x 5) 0) thick‑lines thin‑lines)) (draw (path (point x 0) '‑‑ (point x 10))) (loop (+ x 1)))) ;draw horizontal lines (let loop ((y 0)) (when (<= y 10) ((if (= (modulo y 5) 0) thick‑lines thin‑lines)) (draw (path (point 0 y) '‑‑ (point 20 y))) (loop (+ y 1)))) (thin‑lines))) }
produces
draw‑arrow
is similar to
draw
except it attaches an arrow to the end of
the path. draw‑double‑arrow
attaches arrows to
both the beginning and end of the path.
The thunks quarter‑circle
, half‑circle
and
full‑circle
return the relevant portions of a
circle of diameter unity and center at the origin.
unit‑square
returns a unit square with its lower
left at the origin.
Being so pegged to the origin is not a problem, as
one can transform paths using
the procedures
shift
, rotate
,
rotate‑about
,
reflect‑about
,
slant
,
scale
,
x‑scale
,
y‑scale
, and
z‑scale
[2, ch. 15].
\eval{ (define rectangle (lambda (z1 z4) (let ((x1 (x‑part z1)) (y1 (y‑part z1)) (x4 (x‑part z4)) (y4 (y‑part z4))) (path z1 '‑‑ (point x4 y1) '‑‑ z4 '‑‑ (point x1 y4) '‑‑ 'cycle)))) (picture (lambda () (label 0 "\\vbox{\\hbox{square}\\hbox{peg}}" 'llft) (label 4+3i "\\vbox{\\hbox{round}\\hbox{hole}}" 'urt) ;draw circle of dia = 8 (draw (scale 8 (full‑circle))) ;for square to fit in circle, ;its side must be 8/(sqrt 2) (let* ((s (/ 8 (sqrt 2))) (s/2 (/ s 2))) (draw (rectangle (point (‑ s/2) (‑ s/2)) (point s/2 s/2)))))) }
produces
The procedures x‑part
and y‑part
return the
x- and y-coordinate respectively of their argument
point.
In the following program for a spoked wheel, the path for the spoke is created only once, but we draw several differently rotated versions of it.
\eval{ (set! *unit‑length* (dimen 1 'cm)) (picture (lambda () ;draw two concentric circles for wheel rim (let ((c (full‑circle))) (draw (scale 4 c)) (draw (scale 4.5 c))) (let ((one‑deg (let ((pi (acos ‑1))) (/ pi 180)))) ;one‑deg is 1 degree in radians (let ((spoke (path 0 '‑‑ 2))) ;draw spoke at 20‑deg increments (let loop ((i 0)) (unless (>= i 360) (draw (rotate (* i one‑deg) spoke)) (loop (+ i 20)))))))) }
produces
1 Henceforth, the \centerline{...}
wrapper around the
Scheme code is understood.