Don't talk to me about mathematics -- I've come to the conclusion that I've learnt to live without it.
Prince Philip, The Duke of Edinburgh
There is no 'royal road' to geometry.
Euclid
The turtle procedures are simple to use because they accentuate the intrinsic properties of shapes.
When we study the intrinsic properties of shapes, we look at the angles (which never vary, though the size be doubled), and at the ratios of the size of one side compared to another. To talk of angles and ratios of sides, is to talk of trigonometry.
Enter the following procedure, which is not well-written TG because it uses too many moveto and turnto commands:
4000 DEF PROC_TRI(SIDE)
4010 PROC_MOVETO(200,-200,0) : PROC_TURNTO(30)
4020 PROC_MOVE(SIDE,1) : PROC_TURN(-120)
4030 PROC_MOVE(SIDE/2,1) : PROC_TURN(-90)
4040 PROC_MOVETO(200,-200,1)
4050 ENDPROC : REM TRI
(it is assumed that the basic TG procedures are already there). If the following instant one-line program is entered, eg
FOR I = I TO 7 : PROC_TRI(100*I) : NEXT I
then there are seven superimposed triangles drawn, each getting bigger enclosing the smaller triangles. The angles in the triangle are (bottom) 30 degrees, (top right) 90 degrees, and (top left) 60 degrees; these angles are the same for each triangle -- see Icon 2.1, which shows a similar type of triangle.
The longest of the lines (ie the first to be drawn) is always twice as long as the shortest line (second to be drawn) -- how long (relatively) is the other fine? Hint: use PROC_LOC to work out the coordinates, after the second fine is drawn (ie after fine 4030). What happens if we try to
PRINT SIN(RAD(30)); COS(RAD(60))
should not be very surprising, but has to be explained.
Both SIN(RAD(30)) and COS(RAD(60)) are equal to 0.866025404, and if the value of sine 30 degrees is found from a book of tables, it is 0.8660 (as is the value of cosine 60 degrees). Check that the value of sine 60 degrees (and of cosine 30 degrees) is 0.5. The question now is to relate this to the triangle.
Enter
PROC_RESTART : PROC_TRI(500)
where the coordinates of the lower apex of the triangle are (as one would expect from the content of the routine) 200, -- 200 (X axis, Y axis). By dextrous use of PROC_LOC (twice) it can be found that the upper left apex is at -50,233.012702 with the upper right apex at 200,233.012702.
We know from the routine that the long side of the triangle is 500. This is called the hypotenuse of the right angled triangle (the right angle is that of 90 degrees at the top fight apex). The hypotenuse is always opposite the right angle. The vertical side (ie the medium sized side) is 233.012702 -- 200 -- 433.012702, and the small side is 250 (both from the coordinates, and from the routine, ie 500/2).
If we concentrate on the angle at the lower apex, an angle of 30 degrees, then we can give names to the sides of the right angled triangle. The side opposite the angle (in this case the shortest side) is called the opposite; the side next to the angle, which is not the hypotenuse, is called the adjacent (Icon 2. 1). The sine and cosine of an angle are defined by
sine = opposite/hypotenuse
cosine = adjacent/hypotenuse
so that
sine(30) 250/500 = 0.5
cosine(30) = 433.012702 = 0.866025404
which checks. Check that it checks.
We have been working in degrees, and this is the means by which most of us find it easiest to conceptualise angles. In mathematics (and computer arithmetic) it is easier to work in terms of radians rather than degrees. For example (and do not lose any sleep over it), if x is given in radians
sine(x) = x - x^3/3! + x^5/5 - x^7/7! + . . .
cosine(x) 1- x^2/2! + x^4/4! - x^6/6! + . . .
which is very nice and simple (x^3 -- for example -- is x*x*x and 3! is 1*2*3). So that we can still work in degrees, even though the calculations are simpler for the machine in radians, BB (UG page 331) has a RAD function to change an angle measured in degrees to its equivalent in radians.
To turn completely round is to go through 360 degrees, or to turn through 2*PI radians (PI is a BB constant equal to 3. 14159265, see UG page 318). A radian is also called a circular measure. One radian is the angle at the centre produced by an arc on the circumference of a circle, where the length of the arc is the same as the radius -- a form of circular equilateral triangle.
You would expect that, as the circumference is curved, the other two sides would be slightly closer together than for a normal equilateral triangle. They are. The angle is about 57 degrees for the arc, as against 60 degrees for the equilateral triangle (compare UG page 331). If you know that the length of the circumference is 2*PI*RADIUS, you might like to work out why the radian is equal to 180/PI degrees.
Note that in the TO routines we always use degrees, and the conversion into radians is done within the routines. This is how it should be, though radians have a certain mathematical felicity degrees have an overwhelming familiarity.
The classic three functions of trigonometry are the sine, cosine, and the tangent, with the definition of the tangent being
tangent = opposite/adjacent
which, for our example triangle, produces
tangent(30) = 250/433.012702
= 0.577350269
in agreement with
PRINT TAN(RAD(30))
the way we refer to the tangent in BB (UG page 362). We use all three functions in the TO routines, and, interestingly, in the notes of Kasner and Newman's discussion of 'Change and Changeability' (used last chapter, when examining pathological curves) they give a good brief explanation of what they term trigonometrical 'ratios' (ie functions).
Actually we do not use the tangent in the routines, rather we use the 'arctangent'. The arctangent (written as ATN in BB -- see UG page 210) gives the angle corresponding to a tangent value. T AN(RAD(30)) is equal to 0.577350269, and so ATN(0.577350269) is 30 degrees, but expressed in radians: DEG(ATN( .577350269)) converts the result so that it is given in degrees -- DEC is the reverse of RAD. ACS (arccosine) and ASN (arcsine) are also available in BB (U G pages 201, 209). Another name for the arctangent is the 'inverse' tangent, and similar for the others.
At this point it is worth examining PROC_MOVE and PROC_MOVETO.
If we return to the triangle example, it can be seen that if we start at the lower apex (call it Xl, Yl), then the coordinates of the upper left apex (X2,Y2) are related to the distance between the two apexes (DIST) by
X2 = X1 - DIST*SIN(RAD(30))
Y2 = Y1 + DIST*COS(RAD(30))
If there is no need to remember where are X1 and Y1 then there is no need to distinguish between, say, Xl and X2 in the equations; and if instead of 30 degrees we insert any angle, then we arrive at
X = X - DIST ANCE*SIN(RAD(ANGLE))
Y = Y + DIST ANCE*COS(RAD(ANGLE))
(see PROC_MOVE).
Though this is, at least on the face of it, perfectly acceptable, we do not know that it will work for angles outside the range of 0 degrees to 90 degrees. To prove that it works, we need to find the sign of the trigonometrical ratios for various angles: try it for yourself, and prove to yourself that the equations always work. If the equations do not work, let me know.
To move to a certain position at first appears to be a simple exercise: merely a use of the commands DRAW or MOVE from BB. This is true, but we need to know at what angle the turtle is pointing after the move. We need to know the angle -- this is the key. We know the coordinates, the distances, we do not know the angle.
If we know the sides of a triangle, and we want to know angles, we use the inverse trigonometrical functions: in this case we know the opposite and the adjacent sides, so we use the arctangent. In PROC_MOVETO the local variables XDIF and YDIF correspond to distances along the side of a right angled triangle, in the X and Y directions respectively. The angle (in radians) corresponding to these distances is the arctangent of (XDIF/YDIF), with certain adjustments.
The first adjustment is to try to account for angles outside the range 0 degrees to 90 degrees. If
PRINT DEG(ATN(1)); DEG(ATN(-1))
is tried, the results are 45 and -45 (ie degrees). Relating this to the value of the ratio (XDIF /YDIF) indicates that, when the angle is 45 degrees, both XDIF (equivalent to X2 - Xl) and YDIF (ie Yl - Y2) are negative, so that the ratio is positive. This is correct so far. When both XDIF and YDIF are positive (bottom left corner) the angle is 225 degrees.
For -45 degrees, XDIF is positive and YDIF is negative, so the ratio is negative: again correct. When XDIF is negative and YDIF is positive, the ratio is also negative, but the angle corresponds to 135 degrees.
If YDIF is negative, the angle lies between -90 (270) degrees and 90 degrees (ie is upwards) -- the range of values given by ATN. When YDIF is positive (ie Y1 is greater than Y2) then 180 degrees has to be added to the angle between -90 degrees and 90 degrees -- the adjustment being accomplished by +180*(YN<y) in PROC_MOVE. (On the use of logical comparisons see UG pages 99 to 101, and 369).
The second adjustment is of a different nature: it stops the computer having to divide by zero. In the division XDIF/YDIF, if YDIF is zero (a horizontal fine) then the result is indeterminate, and the computer produces an error. The check in the IF statement is to stop such an error, and after the ELSE the turn is either 90 degrees (if XDIF is negative) or -90 degrees (if XDIF is positive), that is, SGN(-XDIF)*90. FN_ANGLE turns the negative values into the correct positive angle.
A knowledge of trigonometry is not necessary to use TG commands, but to advance in graphics a knowledge of trigonometry is valuable if not totally necessary . In this section we will investigate the drawing of a circle, with an adjustable centre and variable radius.
Start simple. What is a circle? A circle is no more than a polygon with 30 sides -- at least the way we will draw it. 30 sides is too many to easily examine: why not start with a square (a polygon of four sides)? Two calls to PROC_PERIFIXED :
PROC_PERIFIXED(1600,4) : PROC_PERIFIXED(-1600,4)
to produce the arrangement of Icon 2.2. If instead of four sides we request 30 sides, then we also get two slightly skewed circles (only the skewness of the squares amplified).
To draw circles, we need to 'de-skew' them, only we do not de-skew the 30 sided polygons at first: we start simple, and de-skew the squares. To de-skew the squares all we need to do is tilt the squares through 45 degrees:
PROC_TURN(45) : PROC_PERIFIXED(1600,4) :
PROC_PERIFIXED(-1600,4)
This arrangement is shown in Icon 2.3, with the addition of a few extra fines and labels. The dotted line from a to d (ie ad), is at an inclination of 0 degrees; the line ab is at 270 degrees; the fine be is at 45 degrees; and the line an is at 3 1 5 degrees.
These angles are only true for a square, but certain intrinsic properties are true for all polygons:
ac = ab*sine(abc)
angle(cad) = angle(abc)
so that if ab is the radius, then the distance through which the turtle moves at each side is twice the length of ac. The distance is thus 2*ac = 2*radius*sine(abc) -- the angle a be for a 30-sided polygon is 360/30*2 = 6 degrees (why?). The distance moved along each side is thus 2*radius*sine(6). We also have to start by turning through six degrees.
It makes sense to work through the preceding argument, if it is not too clear, because this is the justification of PROC_C30 (shown in Turtle Routines 1.2). The routine is called C30 as it draws a circle by actually drawing a 30-sided polygon.
The local variable J refers to the size of each side of the polygon, where R is the radius, and the local variable I is a loop counter. The routine assumes that the turtle is at the centre of the circle to be drawn, but that the angle at which the turtle is facing is unknown. The turtle moves forward a distance R without drawing, and is turned through 96 degrees (ie to the left 90 degrees and down six degrees from this).
The circle is drawn in a conventional manner as a 30-sided polygon, where the move is J forward and the turn is through 12 degrees. Finally the turtle turns to point back, returns to the centre of the circle, and is then left pointing in the original directIon.
I suggested that when investigating the random walk that different values of the PLOT parameter be tried, to see their differing effects. The MOVE and MOVETO routines in Version 1.1 only offer two styles -- draw a line, or do not draw a fine -- so why not expand the scope to use more of the BB plotting variants?
In Turtle Graphics Version 1.2 most of the routines are the same, the only differences come in the two moving commands. If the UG (pages 3 I 9, 320) is consulted, it can be seen that plotting commands come in groups of eight: within each group of eight we want the sixth command -- which always draws a fine absolute in the current graphics foreground colour.
Wanting to keep the commands MOVE and MOVETO compatible with those of Version I. I, I decided that 0 should remain as move, do not draw, and kept I as draw a fine. In terms of the plotting commands, 0 in MOVE corresponds to 0 in PLOT, and I in MOVE corresponds to 5 in PLOT. This explains how the plotting mode is calculated in the new moving commands -- the style parameter of the moving commands, and their action, are shown in Figure 2. I.
Figure 2.1 Drawing Styles
MOVE | PLOT | ACTION |
0 | 0 | Just move |
1 | 5 | Draw line |
2 | 13 | Draw line without last point |
3 | 21 | Dotted fine |
4 | 29 | Dotted fine without last point |
9 | 69 | Plot point |
11 | 85 | Fill triangle |
It is also possible to use the PLOT parameters directly as a parameter in MOVE and MOVETO, but using my method is fairly simple, and comprehensive: if you want to do more, then do so.
These different styles are illustrated by Icons 2.4 to 2.8. All these examples are obtained by use of the same routine PROC_SINFN, with different parameters. PROC_SINFN is an example of the use of TG to provide 'polar' plots: a polar coordinate of a point is the distance of the point from the origin, together with the angle at which the point is in relation to the origin (sounds very TG?).
PROC_SINFN relates the angle of inclination to the distance away from the origin by some function of the sine of the angle, with the factor being variable. The first example (Icon 2.4) shows a 'cardoid', with a factor of 1/2 and a plotting style of 11 (ie PLOT with 85, filling in triangles, effectively filling the shape). Icon 2.5 also fills triangles, and the factor is 8.
Icon 2.6 uses a style of 9 (ie PLOT with 69), that is, plot a point: the factor is less than 1/2, I will let you discover which value it is. A dotted line (style 3, PLOT with 21) is shown in Icon 2.7, the factor is greater than I, but work it out (note the differences between even and odd factors greater than I). The use of the dotted fine produces some interesting interference patterns.
Last of all, we have another circle -- Icon 2.8. In this case the style is I (PLOT with 5) -- draw a fine. It reinforces the relationship between trigonometrical ratios, and shapes in general. The interference patterns are again quite intriguing, and also known as Moire Effects.
The time has now come to move up a gear, to a mode of many colours.
1000REM-------------------------------
1010
1020
1030REM G R A P H I C ART
1040
1050REM (c) Boris Allen, 1983
1060
1070
1080REM-------------------------------
1090
1100REM Turtle Graphics : 1.2
1110
1120REM-------------------------------
1130
1140 DEF PROC_CLRSCR
1150 PROC_CLS : PROC_CLG
1160 ENDPROC : REM CLRSCR
1170
1180 DEF PROC_CLG
1190 GCOL 0,PEN : GCOL 0,129-PEN
1200 VDU 24,0;128;1279;1023; : CLG
1210 REM Clears an upper graphics windo
w
1220 VDU 29,640;566;
1230 REM Sets the origin to centre of g
raphics window
1240 ENDPROC : REM CLG
1250
1260 DEF PROC_CLS
1270 COLOUR 1-PEN : COLOUR 128+PEN
1280 VDU 28,0,31,39,28 : CLS
1290 REM Clears lower text window
1300 ENDPROC : REM CLS
1310
1320 DEF PROC_COL(PE)
1330 PEN=PE
1340 ENDPROC : REM COL
1350
1360 DEF PROC_CENTRE
1370 MOVE 0,0 : ANGLE=0 : X=0 : Y=0
1380 ENDPROC : REM CENTRE
1390
1400 DEF PROC_RESTART
1410 PROC_CLG : PROC_CENTRE
1420 ENDPROC : REM RESTART
1430
1440 DEF PROC_START
1450 PROC_COL(0) : PROC_CLRSCR : PROC_C
ENTRE
1460 ENDPROC : REM START
1470
1480 DEF PROC_INVERT
1490 PEN=1-PEN : GCOL 0,PEN
1500 ENDPROC : REM INVERT
1510
1520 DEF PROC_TURNTO(A)
1530 ANGLE=FN_ANGLE(A)
1540 ENDPROC : REM TURNTO
1550
1560 DEF PROC_TURN(A)
1570 ANGLE = FN_ANGLE(ANGLE+A)
1580 ENDPROC : REM TURN
1590
1600 DEF PROC_LOC
1610 PRINT "COORDINATES ARE ";X,Y'"ANGL
E IS "ANGLE
1620 ENDPROC : REM LOC
1630
1640 DEF PROC_MOVE(DISTANCE,STYLE)
1650 X=X - DISTANCE*SIN(RAD(ANGLE))
1660 Y=Y + DISTANCE*COS(RAD(ANGLE))
1670 IF STYLE<>0 THEN PLOT (STYLE-1)*8+
5,X,Y ELSE MOVE X,Y
1680 ENDPROC : REM MOVE
1690
1700 DEF PROC_MOVETO(XN,YN,STYLE)
1710 LOCAL XDIF,YDIF : XDIF=XN-X : YDIF
=Y-YN
1720 IF YDIF<>0 THEN PROC_TURNTO(DEG(AT
N(XDIF/YDIF))+180*(YN<Y)) ELSE PROC_TURN
TO(SGN(-XDIF)*90)
1730 X=XN : Y=YN
1740 IF STYLE<>0 THEN PLOT (STYLE-1)*8+
5,X,Y ELSE MOVE X,Y
1750 ENDPROC : REM MOVETO
1760
1770 DEF FN_ANGLE(A)
1780 IF A MOD 360 <0 THEN =A MOD 360 +
360 ELSE =A MOD 360
1790 REM ANGLE
1800
1810 DEF PROC_NEW
1820 VDU 26 : CLS
1830 ENDPROC : REM NEW
3000REM-------------------------------
3010
3020
3030REM G R A P H I C ART
3040
3050REM (c) Boris Allen, 1983
3060
3070
3080REM-------------------------------
3090
3100REM Turtle Routines : 1.2
3110
3120REM-------------------------------
3130
3140 DEF PROC_C30(R)
3150 LOCAL I,J
3160 J = 2*R*SIN(RAD(6)) : PROC_TURNTO(
0) : PROC_MOVE(R,0) : PROC_TURN(90+6)
3170 FOR I = 1 TO 30 : PROC_MOVE(J,1) :
PROC_TURN(12) : NEXT I
3180 PROC_TURN(90-6) : PROC_MOVE(R,0) :
PROC_TURN(180)
3190 ENDPROC : REM C30
3200
3210 DEF PROC_SINFN(SIZE,FACTOR,STYLE)
3220 LOCAL I,MAX
3230 IF FACTOR <= 1 THEN MAX=180/FACTOR
ELSE MAX=180-(INT(FACTOR/2)*2=FACTOR)*1
80
3240 FOR I=1 TO MAX : PROC_CENTRE : PRO
C_TURNTO(I) : PROC_MOVE(SIZE*SIN(RAD(I*F
ACTOR)),STYLE) : NEXT I
3250 ENDPROC : REM SINFN