So far we have looked only at programs that proceed from one command to another using each only once. Although this has made our first few programs easy to follow, it does not demonstrate the full potential of GLE as a programming language. In the following section we will develop the drawing routines already studied within the context of a full programming language. We shall look at how, by reusing code and defining subroutines, we can greatly simplify the construction of a complex program. These concept will be familiar territory to anyone with a knowledge of other high level programming languages such as C or Pascal.
To begin with we formalize some of the syntax we have been using. We define a command as a keyword that prepares GLE for a task. Following this is a set of qualifiers that define the parameters of the task; the width and height of a box or the coordinates of a point for example. We also distinguish between qualifiers that are single variables and those that are commands in their own right. The instruction set to draw a box may be written as:
box 2 4 fill grey20 justify CR
We see that 2 and 4 are single variables that are required to draw the box successfully. The other qualifiers take the form of a sub command followed by another qualifier, these are optional - removing them will result in the box being drawn with the default settings. The order of the sub-commands does not matter but we cannot separate command and qualifier, nor can we place the sub-commands before the single variables, the 2 4
must not be separate from the box
command.
When GLE finds a keyword in the script it looks to the right of that word for any qualifiers that are necessary, then to the right of those for any optional sub-commands and their qualifiers. GLE ignores all multiple spaces, though each word must, at least, be separated by a single space. The end of line character is a signal to GLE that the current command sequence has ended and it should proceed to the next line of code.
The set
command is a keyword and, if we have several variables to specify at the same time, then we may shorten the usage to a single line. Suppose we have the code:
set hei 3
set color red
set just center
set font ss
GLE reads the keyword set
; sets the font height to 3, then forgets about the set command. In the second line it follows the same process but sets the colour to red. It makes sense that we can shorten the code to a single set
command that we write as,
set hei 3 color red just center font ss
Which accomplishes the same thing but using the command to sub-command system.
So far we have explicitly provided values for each command we have used. For example, when drawing a box we gave numerical values for both the height and width. It is useful, however, to be able to define a variable that represents a number or a string of text. For example we may write:
a=3.1415
b=3
box a b
Which tells GLE to draw a box of width ‘a
’ and height ‘b
’, where ‘a
’ and ‘b
’ are defined to have the numerical vales 4 and 3.1415. If a variable is not defined before it is used in the program then it will take the default value of zero.
Any combination of letters and numbers may be used to represent a numerical variable, it makes sense to choose ones that are both memorable and sensible. Text strings can be represented by variables that end in a ‘$’ sign, text$
for example. This works fine for most commands but the text
command treats text strings as plain text. For example, the set of commands:
a$=Hello
text a$
Will write the text ‘a$’ to the screen. To display the variable text we use the command write
, the only difference from text
being the ability to detect and display text strings.
text1$=color
text2$=red
set color text2$
write This is the text1$ text2$ text3$
The string variable ‘text3$
’ is undefined at this point in the program. On fininding this GLE will simple disply the plain text ‘text3$’ . The write
command does not preclude the use of $ signs, just any defined string variable containing them.
GLE contains and extensive library of built in functions which may also be used to simplify a program. Many of the expressions are mathematical, including all the standard operations: ‘+’ for add, ‘-’ for subtract, ‘*’ and ‘/’ for multiply and divide. We may include Boolean operators (AND, OR) and relational operators (<, >, =>, <=, =, <>). There are also trigometric and exponential functions, a set of functions for manipulating strings of text, and functions that return the current date and time. A full listing of the available expressions is given in the appendix.
Wherever a string or numerical value is used, an expression can also be used. Equally, numerical and string variables can be substituted into an expression. The following lines of code will draw three identical lines:
rline 3 2
rline 9/3 sqrt(4)
a=9
b=3
c=1+1
rline a/b sqrt(2*c)
Expressions in GLE are delimited by white space and therefore we must not include any spaces within the expressions themselves. Thus, rline 3*2 4
is a valid expression, whilst rline 3 * 2 4
is not.
An important part of GLE programming is the use of programming loops and the logic that is needed to execute them. Suppose we have a repetitive task whose execution differs slightly each time. Instead of writing out each individual step we use a loop, a repeated piece of code that runs until the conditions specified at the top of the loop are satisfied.
We look at a simple example of the use of a loop
circles.gle size 20 20 amove 10 10 for x = 0.01 to 100 step 0.01 circle 1/x next x
for
command and the next x
command, everything between these is part of the loop. The three qualifiers that follow this command determine the number and spacing of the loops, consider the loop,
for x = a to b step c
(....)
next x
On reaching this line of code GLE sets x
equal to a
and then repeats the following procedure,
x
is greater that b
then the loop is skipped and the command immediately following the loop is exectuted.c
is added to x
for
and next
commands is executedThere is nothing special about x, it can be any variable we wish. Notice,
however, that the variable used in a loop is not a local variable as with
a subroutine. If we define x
in the code
preceding the loop then in the following code the value of x
will be the last value it took in the loop.
If...then
statementsA similar command can be used to execute different code depending on whether a given statement is true or not.
if xpos()=3 then
text We are at x=3
else
text We are not at x=3
end if
If the statement immediately following the if
command is true then GLE executes the code between the then
command and the else
command, if it is false then GLE executes the code between else
and end if
. Either way GLE then moves onto the line of code imediatly following end if
. In this case the program will print ‘We are at x=3’ if the x coordinate is 3, or ‘We are not at x=3’ if it is not.
We look at a more concrete example using both the loop and conditional logic, this illustrates the great advantage GLE has over traditional drawing programs. The following code draws Piet Hein's superellipse, a set of shapes that satify, for different values of n, the equation
!Superellipse.gle - Graphical representation of Piet Hein's superellipse size 24 18 amove 10 9 begin origin !Sets the origin of the ellipse at (10,9) a=5 !a and b represent the excentricity of the ellipse b=8 n=1.5 amove -a -b !Draws the box that represents the limit as n tends to box 2*a 2*b !infinity amove 0 0 for j=0.5 to 10 step 0.25 n=j !n is the exponent of the ellipse for i=0 to 360 step 1 !Calculates the radial distance from the x and y ang=i*3.14/180 c=cos(ang) if (c<0) then c=-c end if s=sin(ang) if (s<0) then s=-s end if ax=(c/a)^n !ax is the projection of the radial coordinate along !the x axis ay=(s/b)^n !ay is the same along the y axis zob=1/(ax+ay) if (zob<0) then zob=-zob end if zub=zob^(1/n) x=zub*cos(ang) y=zub*sin(ang) if i=0 then amove x y else aline x y end if next i next j
Although variables may be defined anywhere within a code it makes sense to place them at or near the start of the program. This allows the variables of the program to easily be changed if the need arises, and also, reduces the number of lines within the central body of a program. Reducing the size of the main programming loop makes the program easier to understand, and to check for errors. A similar technique applies to the writting of subroutines. A subroutine is a distinct section of code that perform a particular task. For example, within a program we may be required to draw a triangle,
amove 2 2
rline 2 0
rline -1 sqrt(3)
rline -1 -sqrt(3)
We can write this section of code at the beginning of the script using the sub
and end sub
commands.
sub triangle
amove 2 2
rline 2 0
rline -1 sqrt(3)
rline -1 -sqrt(3)
end sub
Indentation is optional but can make the program easier to understand. We may call the triangle at any point within the program body using the command,
@triangle
Where the @triangle
commmand tells GLE to execute a subroutine with the name ‘triangle’ .
This has accomplished our goal of simplifying the program body by removing self contained code to the start of the program and giving it a logical call name. It would, however, be far more useful if we could specify the size and position of the triangle each time we draw it. We do this by specifying a series of local variables within the subroutine.
sub triangle x y size
amove x y
rline (size) 0
rline -(0.5)*(size) (0.5)*(sqrt(3))*(size)
rline -(0.5)*(size) -(0.5)*(sqrt(3))*(size)
end sub
Again we may call the triangle at any point in the program,
however we must now specify the size and position of the triangle.
@triangle 2 3 1
@triangle 0.2 sqrt(3.4) 0.5
@triangle a b c-4
The variables used within a subroutine are entirly separate from the program body. They are forgotten as soon as the subroutine is finished; so the size variable in the above line of code is undefined in the program body. We are, of course, free to define a variable ‘size’ and use it within the program body. It will neither affect nor be affected by the execution of the subroutine.
Commonly used subroutines can be removed entirly from the programming script and placed in a separate file which can be made available to any number of GLE programs. The following subroutine substitutes a character (a snowflake) from one of the font tables as a marker for either a graph or diagram.
!subsnow.gle - Subroutine that prints a snowflake sub subsnow size !Size is the font size of the snowflake gsave !Saves the initial graphic state set font pszd hei size t$ = "\char{102}" rmove -twidth(t$)/2 -theight(t$)/2 !Centers the snowflake over the initial coordinates write t$ grestore !Restores the saved graphic state end sub
gsave
and grestore
commands respectively save and restore the current graphic state. When the subroutine is called GLE remembers the initial coordinates and settings of the font height and type. Thus the set command that appears in the subroutine is then not allowed to affect the main program loop, an advantage if we wish to construct a piece of code that will fit into any program we choose.
The rest of the subroutine writes character 102 (a snowflake) from the postscript zapdingbat font at the center of the initial coordinates. The quotation marks around the definition of ‘t$’ indicate to GLE that the string reference is to be taken as a special character and not a literal piece of text. The twidth(t$)
and theight(t$)
are special functions that give the width and height of the character in brackets, if it was printed with the current font size settings.
This piece of code is a perfectly valid subroutine and can be placed at the top of any GLE program. However, if we draw a lot of snowflakes we can reduce the complexity of our GLE script by calling the subroutine from within the program. The above code is placed in a separate file snowflake.gle
. Notice that this is not a valid GLE file, it does not have a size declaration nor any program body. We can however tell GLE that this subroutine is to be compilied with a program using the include
command. We write,
!Snowflake - produces a boxed snowflake size 5 5 include subsnow.gle !Subroutine for drawing a snowflake define marker snow subsnow !Defines a marker to be the snowflake produced !by the subroutine amove 2.5 2.5 box 2 2 just CC marker snow 1 !Draws a 1cm snowflake
The code defines a new marker snow
that draws the snowflake in the subroutine in the same way as any other marker. The box is to show that the snowflake is centered on the initial coordinates.
We have looked at including subroutines in our program, but we can also use the include
command to run any GLE script at any point within another program. Sometimes we may have a very large or complex program that we wish to use again in another script, in this case the include
command can be used instead of copying out the entire file. If we wish to call a very complex program then we can also use the command bigfile
, this is identical to the include
command except the program will be read and compliled one line at a time. This can speed up the execution of complex programs, however there are certain restrictions: some complex multi-line commands will not work and we cannot define subroutines within the bigfile
program.
Frequently used subroutines can be stored in a separate user directory and called by any program. For example the following code will produce a shadowing effect on any given text.
sub ziptext tt$ gsave for i = 1 to 0 step -0.05 set color (i) write tt$ rmove -0.05 0.025 next i set color white write tt$ grestore end sub
!glezip.gle - Demonstrates the ziptext command size 7 5 include ziptext.gle amove 2 1 set font rm hei 2 @ziptext "GLE"
We also look at a final example of the use of subroutines which gives an idea of the maximum processing speed that GLE is capable of. When using programming loops it is a good idea to start with only a small number of iterations, before building up to the number required to produce an accurate looking diagram. It is also important to remember that most postscript viewers are not designed to display very complex diagrams and will not do so with any haste.
!fractal.gle - Draws a fractal size 18 20 rx=1 !Defines the position of the coloured markers ry=1 bx=9 by=16 gx=17 gy=1 amove rx ry !Draws the coloured markers set color red marker dot 0.4 amove bx by set color blue marker dot 0.4 amove gx gy set color green marker dot 0.4 !This set of subroutines moves to halfway between the initial !coordinate and the coloured marker and draws a dot sub green set color green amove (gx+xpos())/2 (gy+ypos())/2 end sub sub blue set color blue amove (bx+xpos())/2 (by+ypos())/2 end sub sub red set color red amove (rx+xpos())/2 (ry+ypos())/2 end sub amove 5 5 for n=1 to 100000 step 1 !Number of iterations defines the speed at !which the program compiles r=rnd(1) !Executes each of the subroutines randomly with if r<0.333 then !the same probability @blue else if r<0.6666 then @green else @red end if end if if n>15 then !Ignores the first few dots which are influenced by marker dot 0.05 !initial start position next n else next n