This next example uses a pair of mutually recursive procedures to draw what is known as a dragon curve (a pretty, space-filling pattern).
MODULE 'intuition/intuition', 'graphics/view' /* Screen size, use SIZEY=512 for a PAL screen */ CONST SIZEX=640, SIZEY=400 /* Exception values */ ENUM WIN=1, SCRN, STK, BRK /* Directions (DIRECTIONS gives number of directions) */ ENUM NORTH, EAST, SOUTH, WEST, DIRECTIONS RAISE WIN IF OpenW()=NIL, SCRN IF OpenS()=NIL /* Start off pointing WEST */ DEF state=WEST, x, y, t /* Face left */ PROC left() state:=Mod(state-1+DIRECTIONS, DIRECTIONS) ENDPROC /* Move right, changing the state */ PROC right() state:=Mod(state+1, DIRECTIONS) ENDPROC /* Move in the direction we're facing */ PROC move() SELECT state CASE NORTH; draw(0,t) CASE EAST; draw(t,0) CASE SOUTH; draw(0,-t) CASE WEST; draw(-t,0) ENDSELECT ENDPROC /* Draw and move to specified relative position */ PROC draw(dx, dy) /* Check the line will be drawn within the window bounds */ IF (x>=Abs(dx)) AND (x<=SIZEX-Abs(dx)) AND (y>=Abs(dy)) AND (y<=SIZEY-10-Abs(dy)) Line(x, y, x+dx, y+dy, 2) ENDIF x:=x+dx y:=y+dy ENDPROC PROC main() HANDLE DEF sptr=NIL, wptr=NIL, i, m /* Read arguments: [m [t [x [y]]]] */ /* so you can say: dragon 16 */ /* or: dragon 16 1 */ /* or: dragon 16 1 450 */ /* or: dragon 16 1 450 100 */ /* m is depth of dragon, t is length of lines */ /* (x,y) is the start position */ m:=Val(arg, {i}) t:=Val(arg:=arg+i, {i}) x:=Val(arg:=arg+i, {i}) y:=Val(arg:=arg+i, {i}) /* If m or t is zero use a more sensible default */ IF m=0 THEN m:=5 IF t=0 THEN t:=5 sptr:=OpenS(SIZEX,SIZEY,4,V_HIRES OR V_LACE,'Dragon Curve Screen') wptr:=OpenW(0,10,SIZEX,SIZEY-10, IDCMP_CLOSEWINDOW,WFLG_CLOSEGADGET, 'Dragon Curve Window',sptr,$F,NIL) /* Draw the dragon curve */ dragon(m) WHILE WaitIMessage(wptr)<>IDCMP_CLOSEWINDOW ENDWHILE EXCEPT DO IF wptr THEN CloseW(wptr) IF sptr THEN CloseS(sptr) SELECT exception CASE 0 WriteF('Program finished successfully\n') CASE WIN WriteF('Could not open window\n') CASE SCRN WriteF('Could not open screen\n') CASE STK WriteF('Ran out of stack in recursion\n') CASE BRK WriteF('User aborted\n') ENDSELECT ENDPROC /* Draw the dragon curve (with left) */ PROC dragon(m) /* Check stack and ctrl-C before recursing */ IF FreeStack()<1000 THEN Raise(STK) IF CtrlC() THEN Raise(BRK) IF m>0 dragon(m-1) left() nogard(m-1) ELSE move() ENDIF ENDPROC /* Draw the dragon curve (with right) */ PROC nogard(m) IF m>0 dragon(m-1) right() nogard(m-1) ELSE move() ENDIF ENDPROC
If you write this to the file `dragon.e' and compile it to the executable `dragon' then some good things to try are:
dragon 5 9 300 100 dragon 10 4 250 250 dragon 11 3 250 250 dragon 15 1 300 100 dragon 16 1 400 150
If you want to understand how the program works you need to study the recursive parts. Here's an overview of the program, outlining the important aspects:
SIZEX
and SIZEY
are the width and height (respectively) of the custom screen (and window).
As the comment suggests, change SIZEY
to 512 if you want a bigger screen and you have a PAL Amiga.
state
variable holds the current direction (north, south, east or west).
left
and right
procedures turn the current direction to the left and right (respectively) by using some modulo arithmetic trickery.
move
procedure uses the draw
procedure to draw a line (of length t
) in the current direction from the current point (stored in x
and y
).
draw
procedure draws a line relative to the current point, but only if it fits within the boundaries of the window.
The current point is moved to the end of the line (even if it isn't drawn).
main
procedure reads the command line arguments into the variables m
, t
, x
and y
.
The depth/size of the dragon is given by m
(the first argument) and the length of each line making up the dragon is given by t
(the second argument).
The starting point is given by x
and y
(the final two arguments).
The defaults are five for m
and t
, and zero for x
and y
.
main
procedure also opens the screen and window, and sets the dragon drawing.
dragon
and nogard
procedures are very similar, and these are responsible for creating the dragon curve by calling the left
, right
and move
procedures.
dragon
procedure contains a couple of checks to see if the user has pressed Control-C or if the program has run out of stack space, raising an appropriate exception if necessary.
These exceptions are handled by the main
procedure.
Notice the use of Val
and the exception handling.
Also, the important base case of the recursion is when m
reaches zero (or becomes negative, but that shouldn't happen).
If you start off a big dragon and want to stop it you can press Control-C and the program tidies up nicely.
If it has finished drawing you simply click the close gadget on the window.
Go to the Next or Previous section, the Detailed Contents, or the Amiga E Encyclopedia.