Variables in PostScript are defined with the def
operator.
To create a variable called pi
, you would use:
/pi 3.1416 defNotice the slash before the variable name. You need this only when you define the variable. Thereafter, you would get the area of a circle of radius 4 like this:
4 4 mul pi mul
.
You use the slash to refer to the variable's name, not its value.
Subroutines are made exactly the same way. However, you normally want more than one command in a subroutine. To do this, you group commands with curly braces. The following defines a subroutine that makes a filled circle:
/filledcircle { gsave newpath 0 0 1 0 360 arc fill grestore } defThis isn't very interesting, however, since this is a very limited subroutine. We can make it take arguments by making it use the previous elements on the stack. So we could set the radius of the circle like this:
/filledcircle { /radius exch def gsave newpath 0 0 radius 0 360 arc fill grestore } defNow, to make a filled circle of radius 1, you would use
1 filledcircle
. Notice how exch
is
used in the variable definition. The stack starts with (if our
argument is 1)
1 /radiusAfter the
exch
we have
/radius 1and now the
def
command will work.
There still is something not quite ideal about our subroutine -
the radius variable is global! You might be overwriting a value used
somewhere else by other code. To fix this, you need to use a
dictionary. This an advanced concept of variable scope
that you will only learn a tiny bit of here. At the
beginning of your subroutine, you can define a new dictionary
with n variables using n dict begin
.
This says, for our purposes, that the next n variables
defined will be local variables. They have scope until the
next end
statement. So here is our routine again.
This time it takes three arguments: x center, y center, and radius.
/filledcircle { 3 dict begin /radius exch def /y exch def /x exch def gsave newpath x y radius 0 360 arc fill grestore end } defNotice how
gsave
and grestore
are used here.
They allow the circle to be drawn without disrupting any path
that might be in progress.
Let us now turn to making a "target" with our filled circles.
This will be a set of nested circles with different grey values.
Obviously we will use a loop to do this, in this case a for
loop. The for
loop takes four arguments: an initial value
for the loop variable,
a step, a final value, and code to repeat. During each iteration
of the loop, the current value of the loop variable is pushed on the stack,
and then the code is repeated. This loop makes a target with 4 circles:
4 -1 1 { gsave dup 1 sub 4 div setgray 0 exch 0 exch 4 div filledcircle grestore } forWe can put this all together in a routine that takes a position, radius and number of circles as its argument.
/target { 5 dict begin /count exch def /radius exch def /y exch def /x exch def count -1 1 { /level exch def gsave level 1 sub count div setgray x y radius level count div mul filledcircle grestore } for end } defHere is the output of
0 0 1 10 target
:
![]() |
|