Treat nature in terms of the cylinder, the sphere, the cone, all in perspective.
Paul Cézanne
You will have noticed that my icons are not exactly a true representation of the pictures you see on the screen. In Icon 5.6 the rays should describe an exact semicircle, but my version is slightly squashed.
The simplest, and most logical, way of transforming shapes is therefore to change the axes, and their relative scaling. In the drawing of a rectangle (as in the last chapter), to draw a rectangle by PROC_RECTANGLE (SIDE,RATIO*SIDE) is to produce a square if RATIO is unity.
If our axes are at right angles, and the distance between P and P + INC is a\ways the same as the distance between Q and Q + INC (for any values of P an& Q), then the axes axes called rectilinear.
This does not mean that the scale of the axes tor both horizontal and vertical is the same, just that for each axis the scale is regular -- rectilinear coordinates are the ones we use when we start coordinate geometry. Such axes are very simple to implement, as long as we concentrate on the intrinsic, and do not get carried away with extrinsic considerations.
Normally, to implement even simple transformations (when a transformation is simply a stretch for the moment) requires the use of transformation matrices -- far too tedious, unartistic, almost arthritic. Examine Turtle Graphics 3.1.
1000 REM-------------------------------
1010
1020
1030 REM G R A P H I C ART
1040
1050 REM (c) Boris Allen, 1983
1060
1070
1080 REM-------------------------------
1090
1100 REM Turtle Graphics : 3.1
1110
1120 REM-------------------------------
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 PROC_TRANSFORMATION(1)
1470 ENDPROC : REM START
1480
1490 DEF PROC_INVERT
1500 PEN=1-PEN : GCOL 0,PEN
1510 ENDPROC : REM INVERT
1520
1530 DEF PROC_TURNTO(A)
1540 ANGLE=FN_ANGLE(A)
1550 ENDPROC : REM TURNTO
1560
1570 DEF PROC_TURN(A)
1580 ANGLE = FN_ANGLE(ANGLE+A)
1590 ENDPROC : REM TURN
1600
1610 DEF PROC_LOC
1620 PRINT "COORDINATES ARE ";X,Y'"ANGL
E IS "ANGLE
1630 ENDPROC : REM LOC
1640
1650 DEF PROC_MOVE(DISTANCE,STYLE)
1660 X=X - DISTANCE*SIN(RAD(ANGLE))
1670 Y=Y + DISTANCE*COS(RAD(ANGLE))
1680 IF STYLE=1 THEN DRAW STRETCH*X,Y E
LSE MOVE STRETCH*X,Y
1690 ENDPROC : REM MOVE
1700
1710 DEF PROC_MOVETO(XN,YN,STYLE)
1720 LOCAL XDIF,YDIF : XDIF=XN-X : YDIF
=Y-YN
1730 IF YDIF<>0 THEN PROC_TURNTO(DEG(AT
N(XDIF/YDIF))+180*(YN<Y)) ELSE PROC_TURN
TO(SGN(-XDIF)*90)
1740 X=XN : Y=YN
1750 IF STYLE=1 THEN DRAW STRETCH*X,Y E
LSE MOVE STRETCH*X,Y
1760 ENDPROC : REM MOVETO
1770
1780 DEF FN_ANGLE(A)
1790 IF A MOD 360 <0 THEN =A MOD 360 +
360 ELSE =A MOD 360
1800 REM ANGLE
1810
1820 DEF PROC_NEW
1830 VDU 26 : CLS
1840 ENDPROC : REM NEW
1850
1860 DEF PROC_TRANSFORMATION(RATIO)
1870 STRETCH = RATIO
1880 ENDPROC : REM TRANSFORMATION
1890
1900 DEF PROC_SQUARE(SIDE)
1910 LOCAL I : FOR I = 1 TO 4
1920 PROC_MOVE(SIDE,1) : PROC_TURN(90)
1930 NEXT I
1940 ENDPROC : REM SQUARE
1950
1960 DEF PROC_SQUAREROT(SIDE,INC)
1970 LOCAL I : FOR I = 0 TO 360 STEP IN
C
1980 PROC_SQUARE(SIDE) : PROC_TURN(INC)
1990 NEXT I
2000 ENDPROC : REM SQUAREROT
2010
2020 DEF PROC_CIRCLE(INC)
2030 LOCAL I : FOR I = 1 TO 30
2040 PROC_MOVE(INC,1) : PROC_TURN(12)
2050 NEXT I
2060 ENDPROC : REM CIRCLE
2070
Essentially Version 3.1 is a simple modification of Version 1.1 (though it is possible to modify Version 1.2 -- try it). The principal differences are to the PROC MOVE and PROC_MOVETO routines, with the addition of a new routine PROC_TRANSFO RMATION. These routines include also three example routines: PROC_SQUARE, PROC_SQUAREROT, and PROC_CIRCLE.
First, the totally new routine PROC_TRANSFO RMATION. This routine has a parameter RATIO, the value of which is assigned to the global variable STRETCH. As it is not possible to use a variable until it has been initialised to some value, PROC_START has to be modified to include the call PROC TRANSFORMATION(0).
The purpose of STRETCH becomes clearer if PROC MOVE is studied. The values of X and Y are calculated as normal, but the plotting is to values which are functions of X and Y. In the case of Y, the function is merely Y, but for X the function is STRETCH*X. The angle is not altered, because X and Y are not altered. This is a 'linear' transformation because a straight line in the old coordinate system is changed to a straight line in the new system.
The changes to PROC_MOVETO mirror the changes to PROC MOVE.
Icon 6.1 shows the operation of PROC_SQUAREROT(200,60), that is, squares of side 200 each turning through 60 degrees from the previous square. A simple enough effect.
If a call is made to PROC_TRANSFORMATION(2), and the squares routine is repeated with the same parameters, then we find the result as shown in Icon 6.2. There are two distinct rectangles (where in Icon 6.1 the squares were parallel to the axes), and several parallelograms.
In linear transformation, squares become parallelograms because straight fines are still straight lines.
To study a slightly different effect, before PROC_SQUAREROT is called we PROC_TURN(-45) to produce Icon 6.3. When transformed as before, the result is as in Icon 6.4. The two squares which in Icon 6.3 are symmetrical about the Y axis are (in Icon 6.4) rhombuses (ie equilateral parallelograms.
When a circle is drawn by PROC_CIRCLE, a circle is drawn when the transformation is 1 (in Icon 6.5 the circle is a little squashed). As soon as the transformation is 2, the result is that of Icon 6.6. An ellipse is no more than a stretched (or squashed) circle.
Icon 6.7 shows another transformed circle, when the transformation is not linear.
The 'circle' in Icon 6.7 was drawn using the Turtle Graphics 3.2 routines. These routines are again modifications to Version 1.1, but with a great number of changes.
1000 REM-------------------------------
1010
1020
1030 REM G R A P H I C ART
1040
1050 REM (c) Boris Allen, 1983
1060
1070
1080 REM-------------------------------
1090
1100 REM Turtle Graphics : 3.2
1110
1120 REM-------------------------------
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 X=0 : Y=0 : MOVE FN_XAXIS(0),FN_YA
XIS(0) : ANGLE=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 LOCAL I,IX,IY,SX,SY : SX = X : SY
= Y
1660 SX=X - DISTANCE*SIN(RAD(ANGLE))
1670 SY=Y + DISTANCE*COS(RAD(ANGLE))
1680 IF ABS(X-SX)>ABS(Y-SY) THEN D = IN
T(ABS(X-SX)/12)+1 ELSE D = INT(ABS(Y-SY)
/12)+1
1690 IX = (SX-X)/D : IY = (SY-Y)/D
1700 FOR I = 1 TO D
1710 X = X+IX : Y = Y + IY
1720 IF STYLE=1 THEN DRAW FN_XAXIS(X),F
N_YAXIS(Y) ELSE MOVE FN_XAXIS(X),FN_YAXI
S(Y)
1730 NEXT I
1740 X = SX : Y = SY
1750 ENDPROC : REM MOVE
1760
1770 DEF PROC_MOVETO(XN,YN,STYLE)
1780 LOCAL XDIF,YDIF,D,I : XDIF=XN-X :
YDIF=Y-YN
1790 IF YDIF<>0 THEN PROC_TURNTO(DEG(AT
N(XDIF/YDIF))+180*(YN<Y)) ELSE PROC_TURN
TO(SGN(-XDIF)*90)
1800 IF ABS(XDIF)>ABS(YDIF) THEN D = IN
T(ABS(XDIF)/12)+1 ELSE D = INT(ABS(YDIF)
/12)+1
1810 XDIF = (XN-X)/D : YDIF = (YN-Y)/D
1820 FOR I = 1 TO D
1830 X = X + XDIF : Y = Y + YDIF
1840 IF STYLE=1 THEN DRAW FN_XAXIS(X),F
N_YAXIS(Y) ELSE MOVE FN_XAXIS(X),FN_YAXI
S(Y)
1850 NEXT I
1860 X=XN : Y=YN
1870 ENDPROC : REM MOVETO
1880
1890 DEF FN_ANGLE(A)
1900 IF A MOD 360 <0 THEN =A MOD 360 +
360 ELSE =A MOD 360
1910 REM ANGLE
1920
1930 DEF PROC_NEW
1940 VDU 26 : CLS
1950 ENDPROC : REM NEW
1960
1970 DEF FN_XAXIS(COORD)
1980 =COORD
1990 REM XAXIS - modify preceding line
2000
2010 DEF FN_YAXIS(COORD)
2020 =COORD
2030 REM YAXIS - modify preceding line
2040
2050 DEF PROC_SQUARE(SIDE)
2060 LOCAL I : FOR I = 1 TO 4
2070 PROC_MOVE(SIDE,1) : PROC_TURN(90)
2080 NEXT I
2090 ENDPROC : REM SQUARE
2100
2110 DEF PROC_TRIANGLE(SIDE)
2120 LOCAL I : FOR I = 1 TO 3
2130 PROC_MOVE(SIDE,1) : PROC_TURN(120)
2140 NEXT I
2150 ENDPROC : REM TRIANGLE
2160
2170 DEF PROC_CIRCLE(INC)
2180 LOCAL I : FOR I = 1 TO 30
2190 PROC_MOVE(INC,1) : PROC_TURN(12)
2200 NEXT I
2210 ENDPROC : REM CIRCLE
2220
2230 DEF PROC_LINES
2240 LOCAL I
2250 FOR I = 0 TO 90 STEP 2
2260 PROC_MOVETO(-450,-300,0) : PROC_TU
RNTO(-I) : PROC_MOVE(1500,1)
2270 NEXT I
2280 ENDPROC : REM LINES
What has to be performed is actually rather mundane. All that is needed is to set up two functions (one for the X axis and one for the Y axis) which convert the actual values of X and Y to screen coordinates. The change from the actual value of X to the screen value of STRETCH*X is a simple example.
Start at the beginning. Going through the routines, the first to be altered is PROC_CENTRE and it is altered in two ways. The initialisations of X and Y are placed at the beginning of the routine (rather than at the end) because sometimes the function routines use X and Y explicitly. The function routines FN_XAXIS and FN_YAXIS initialise the cursor to the centre of the transformed screen.
The real complexities arise in the coding of PROC_MOVE -- complexities which ease the way for the user. We need many more local variables, because of the storage of many more interim values. The variables SX and SY are calculated to be the endpoints of the line to be drawn, by the same method as before.
To draw a fine between two points in a rectilinear coordinate geometry is to draw a straight fine (a pretentious way of saying that in ordinary geometry the shortest distance between two points is a straight fine). To draw a line between two points on the surface of the Earth is to draw a curve -- though at each point you may think that you are following a straight line. (See for example, Klein's (1953) chapter on 'New Geometries, New Worlds'.)
To draw the shortest fine between two points is to follow a 'geodesic', and the path depends on the geometry. In ordinary geometry the path is a straight fine, in some of the other geometries we will investigate, it is anything but straight. The way we draw a line is a portion at a time, at each time producing a straight fine. As we found with a circle in ordinary geometry, a series of straight fines can produce a curve.
We have to decide on how many little straight fines we need to draw. We decide this by noting that if we move (slowly!) at about three pixels at a time, the fine is an almost perfect curve. if the difference in the X direction is greater than the difference in the Y direction, then the X difference is divided by 12 units to produce the number of steps (D) (else the difference is used).
When D has been calculated, D is used to work out the increments in the X and Y directions (ie IX and IY). These increments are then used to plot D straight fines, where the plotting is to FN_XAXIS(X),FN_YAXIS(Y). The end values (SX and SY) are then assigned to X and Y, to prevent the overaccumulation of rounding errors.
PROC_MOVETO in this version is far simpler to understand than PROC_MOVE, particularly when PROC_MOVE has been studied.
The transformations appear at FN_XAXIS and FN_YAXIS, and by default they do nothing other than return the value of X and Y.
Icon 6.7 was drawn with a strange geometry:
DEF FN_XAXIS(COORD) = SGN(COORD)*COOREY2/500
DEF FN_YAXIS(COORD) = SGN(COORD)*SQR(ABS(COOR
D)) *20
which stretches the scale more at larger values of X, and squashes the scale more at larger values of Y. These two coordinate transformations give a very non-rectilinear geometry.
Icon 6.8 shows two squares. One appears as a rectangle, and this is a square parallel to the axes. The other is a square which is symmetrical around the X axis. The routine to draw the square is PROCL-SQUARE.
If PROC_LINES is activated when the coordinates are rectilinear (ie both functions = COORD) then we have a series of straight lines radiating from the point -400,-300 in the upper right quadrant. It is a trifle like the showers example, and has similarities to the Moire example in the Version 2.1 graphics.
Icon 6.9 has no resemblance to anything, so it would appear. Each fine radiating from the point -400,-300 is a geodesic -- the shortest distance between two points in the geometry defined above. What happens when a line is drawn?
At each stage of the curve there is a short straight line which follows in the direction given by the geometry at that point. As with the circular geometry (straight line, turn, straight line, turn) a curve then appears -- with the straight portions carefully hidden by our eyes. Of course, a straight fine is only a special form of curve.
With this pair of axis transformations we have effectively defined a 'force field', and our turtle follows the fines of force between points. Get the axes correctly defined, and we might have an Einsteinian force field. (Remember Abelson and diSessa?) Let us examine this force field.
The lines radiate from the bottom left corner, starting straight upwards and moving clockwise. Until the fines reach the Y axis they look reasonably uncurved. They look mainly straight until the line crosses the axis (note that crossing the X axis does not seem to be at all traumatic). At the Y axis something strange happens.
As the fines become closer to the Y axis, so they become more bent and seem almost determined not to cross the axis. The fines come very close together, and once past the axis they change direction, and appear more at fight angles to the axis. The effect is strange to watch.
Once over the other side, there seems to be abnormal behaviour about the region of the X axis (though is any behaviour normal?). To go through the X axis on the left side of the Y axis does not produce any kinks in the fines. For some fines, a kink appears on the positive side. Setting the starting point to other coordinates will produce different effects.
All this reveals the truth of Kasner and Newman's quote (1949 page 163) '. . . -- our intuitive notions about space almost invariably lead us astray'. Funnily, they were talking of geodesics, but they were interested in spiders not turtles.
Icon 6.10 is merely a sine curve. The interesting thing about that sine curve is the way in which it was drawn.
The functions were defined by
DEF FN_XAXIS(COORD) = COORD
DEF FN_YAXIS(COORD) = 200*SIN(RAD(X))
or, perhaps more illuminatingly,
DEF FN_XAXIS(COORD) = X
DEF FN_YAXIS(COORD) = 200*SIN(RAD(X))
and to draw that line I entered
PROC_MOVETO(-600,0,0) : PROC_MOVETO(600,0,1)
which moved the turtle/cursor to X equal to -600, Y equal to 200*SIN(RAD(X)), and not Y = 0. The value in the second parameter of PROC_MOVETO is a dummy, that is, it is not used as such, it has been by-passed by the function definition.
A line was then drawn to X = 600, a geodesic in the sine geometry.
We now have a way to draw graphs of functions, but very easily. To draw a parabola (ie X = k*Y^2) we use the definitions
DEF FN_XAXIS(COORD) = Y
DEF FN_YAXIS(COORD) = Y^2/400
and produce Icon 6.11. This is an extremely flexible way of drawing graphs. To produce the parabola I entered
PROC_MOVETO(0,400,1)
and half of the parabola (the upper half) is drawn. When I then entered
PROC_MOVETO(0,-400,1)
the upper is retraced and the lower half drawn in. This way of analysing functions is clearly of great assistance, and fits well with turtle graphics, especially Version 3.2. We use the fact that the graph of a function is geodesic, the fine between two points, as given by the geometry (ie the axes).
The technique we are now using is called the 'parametric form' of displaying graphs. This technique, which in many ways is simpler than other forms, is not taught at lower levels in schools -- being somehow more 'difficult' or 'esoteric'. The parametric form is used quite extensively in 'advanced' graphics, it is one way of simplifying, or, as we would say, accentuating the intrinsic and minimising the effects of the extrinsic.