Ian O. Angell, Brian J. Jones (Auth.) - Advanced Graphics With The Sinclair ZX Spectrum-Macmillan Education UK (1983)
Ian O. Angell, Brian J. Jones (Auth.) - Advanced Graphics With The Sinclair ZX Spectrum-Macmillan Education UK (1983)
The paperback edition of this book is sold subject to the condition that it
shall not, by way of trade or otherwise, be lent , resold , hired out,or otherwise
circulated without the publisher's prior consent in any form of binding or
cover other than that in which it is published and without a similar condition
including this condition being imposed on the subsequent purchaser.
Contents
Preface ix
Introduction
Aims of the book. Motivation and format. How to approach the contents.
Three levels: example programs, a program package or a textbook. A substantial
example to illustrate what can be drawn after reading the contents of this book.
15 Projects 235
Ideas for extended programs in computer graphics.
Contents vii
Index 246
With the rapid advance of computer technology has come a substantial reduction
in the price of computer hardware . In the coming years the price of peripheral
devices will also tumble. This means that users with a limited budget , who
previously had access only to the most elementary computing devices, will soon
be able to afford the most sophisticated computers. They will also be able to
escape from the limitation of tabular numerical output and buy microprocessor
attachments for television monitors or inexpensive special-purpose colour
graphics devices. Sinclair computers have always led the field in this respect.
Software, however, does not appear to be getting cheaper.
Because of the enormous capital expend iture required to set up graphical
output in the past, both in machines and software, the subject of computer
graphics has been the preserve of large research groups. This inaccessibility has
led to a mystique growing up around the subject and it has achieved a false
reputation for difficulty. This book is an attempt to lay the ghost of com-
plexity; it will also show that complicated (and hence expensive) software
packages, which are naturally of great value in research organisations, need not
frighten away the average computer user. For most purposes these packages are
unnecessary. This book, as well as being an introduction to computer graphics,
may be considered a (very inexpensive) software package: it is a lot cheaper
than commercially available packages! Naturally, because of this fundamental
approach, users have to achieve a reasonable understanding of their graphics
device before pictures, other than those provided, may be drawn . This need
not be a disadvantage ; the amount of groundwork required will be seen to be
very limited. As a direct result , the knowledge of the user grows along with the
package and he is far less likely to misinterpret any of the graphical routines.
References are given and relevant further reading material is recommended in
order to expand the reader's horizons in the subject.
It is assumed that the reader has an elementary knowledge of Cartesian
coordinate geometry (the authors recommend the books detailed in Cohn
(1961, Coxeter (1974), and McCrae (1953) - see References), and also of the
BASIC programming language (see the Spectrum BASIC Handbook (Vickers
(1982) and Hurley (1982»). Many interesting programming exercises are proposed,
and these should raise the standard of the reader's BASICexpertise. BASIC is a
universally popular language, available (in various guises) on all types of micro-
computer, so the programs may be easily adjusted to run on machines other
x Preface
than the Spectrum : it is also a good medium for transmitting the algorithms
used in computer graphics, enabling readers to translate these ideas readily
into any other computer language of their choice .
The concepts necessary for the study of computer graphics are organised as
a combination of theory and worked examples ; these are introduced as and
when needed in the natural progression of the subject. Some program listings
form part of the examples and these should not be considered just as algorithms
that describe solutions to fundamental graphical problems, but also as a computer
graphics software package in BASIC, or simply as programs to draw patterns.
Alongside the examples are a series of exercises that expand these ideas. The
practical problems implicit in programming the various concepts of computer
graphics are often more a source of difficulty to the student than the concepts
themselves. Therefore it is essential that readers implement many of the program
listings given in the book in order to understand the algorithms, as well as
attempt a large number of exercises. As an extra learning aid, a companion audio-
cassette tape is being made available; this contains most of the larger program
listings given in this book . If readers are nervous of the mathematics, they should
run the programs first before studying the theory.
This approach to the subject has been used with great success in teaching
computer graphics to undergraduates and postgraduates at Royal Holloway
College. Quickly producing apparently complex pictures results in the positive
feedback of enthusiastic interest. The ability to construct pictures on line-
drawing and colour interactive graphics screens makes a long-lasting impression
on students; and the step-by-step approach brings them very quickly to the
level of very sophisticated computer graphics. That level is outside the scope
of this book, but where necessary readers will find relevant references to guide
them into the more advanced topics.
This book is aimed at those who are competent BASIC programmers but
complete beginners in graphics. It contains the elementary ideas and basic infor-
mation about pixel and two-dimensional graphics, which must be mastered
before attempting the more involved ideas of character and three-dimensional
graphics. This is followed by a section relating to character graphics and the
display of data (in line drawings and colour) - probably the most important
non-specialised, commercial use of computer graphics. Later chapters introduce
the reader to the geometry of three-dimensional space , and to a variety of
projections of this space on to the two-dimensional space of graphics devices.
The related problems of hidden lines and hidden surfaces, as well as the con-
struction of complex three-dimensional objects, are dealt with in detail. Finally,
we return to advanced ideas in BASIC programming and a large worked example
of a video game.
Graphics is one of the most rapidly expanding areas of computer science. It
is being used more and more in the fields of Computer Aided Design (CAD),
Computer Assisted Management (CAM) and Computer Assisted Learning (CAL).
At one time it was only the big corporations such as aircraft and automobile
Preface xi
manufacturers that used these techniques, but now most companies are realising
the potential and financial savingsof these ideas. What is more, not only is
computer graphics profitable, it's fun! The Sinclair Spectrum is an ideal machine
on which to learn the basics of computer graphics, and an excellent springboard
to the most sophisticated (and expensive) graphics devices.
We hope this book will display some of the excitement and enthusiasm for
computer graphics experienced by us, our colleagues and students. To demon-
strate just how useful computer drawings are for illustrating books and pam-
phlets, all the pictures here were drawn by computer specifically for this book.
Introduction
This book may be read at a number of different levels. Firstly, it can be considered
as a recipe book of graphics programs for those who simply want to draw
complex pictures with their Spectrum. We naturally hope that the reader, having
drawn these figures, will be inspired to delve deeper into the book in order to
understand how and why the programs were constructed. Secondly, some of the
programs may be used as a package to produce and label data diagrams (pie-charts,
histograms and graphs) for business and laboratory use. Finally, and the main
reason for writing the book, it is an introductory text to computer graphics,
which leads the reader from the elementary notions of the subject through to
such advanced topics as character graphics, construction of three-dimensional
objects and hidden line (and surface) algorithms.
The complex programs later in the book are much too involved to be given as
a single listing. Furthermore we will see a great deal of repetition in the use of
elementary algorithms. Therefore we use the top down or modular approach in
writing and explaining programs. The solution to each major graphics problem is
conceived as a series of solutions to subproblems. These subproblems may be
further broken down into a set of problems to be solved (modules). These
modules will be programmed in the form of BASIC subroutines. Each is given an
identifier (in lower case characters) and will solve a particular subtask . Then the
totality of submodules combine to solve the required graphics problem. Because
the program listings are used to represent algorithms for the solution of these
subtasks, we decided in general not to use statements like GO SUB 6000. We
prefer instead to assign the subroutine identifier to the address value at the
beginning of the routine (for example, LET scene3 = 6000) and thus we can
write statements like GO SUB scene3. Weuse lower case for subroutine identifiers
(and groupings of routines in the text) only : all other program variables will be
in upper case to avoid confusion.
Spectrum BASIC does not have the facility of passing parameters into routines.
Values of input parameters have to be set in assignment statements outside the
routine , and the names of output parameters must be known if sensible use is to
be made of the routine . This can be rather inconvenient if you are using someone
else's package of routines. It is essential that users know the names of the input
and output parameters ; therefore in our routines we use the REMarks IN: (to
identify the INput parameters) and OUT: (for the OUTput parameters). We
number our programs so that all program statements are on lines ending in 0,
2 Advanced Graphics with the Sinclair ZX Spectrum
and REMarks on lines ending in 1 to 9 (except the naming of routines) . The IN:
and OUT: REMarks follow directly the naming REMark on lines ending in 1 and
2 respectively. Also the cassette tape listings of programs use character codes to
highlight and colour various REMarks (see chapter 13). In cases where we think
that the word REM detracts from readability of a line we use these codes to
make it invisible. Wehave minimised the REMarks on the cassette so that we can
pack the maximum amount of program listing on to the tape. It is a good idea to
expand these listings by adding the complete REMarks, and SAVE them on your
own tapes.
For those who want only to run our programs, we give a list of complete
programs at the end of each chapter together with suitable data values. In fact
it is a good idea for all, including the serious readers, to SAVE the routines on
tape before approaching each chapter. They can then LOAD, MERGE and RUN
the programs as they occur in the text. The cassette tape available to accompany
the text contains all the larger listings in the book, as well as BYTE data for
diagrams and character sets used in later programs (which would otherwise have
to be constructed by readers themselves, a rather time-consuming process). Our
routines were written for the 48K Spectrum: if you have a 16K machine you
should read appendix A and note the changes that need to be made.
As an example of what to expect, we give below the program required to
draw figure I.1 , a line drawing of a body of revolution in which all the hidden
lines have been suppressed. This will work on both types of machine .
Figure 1.1
The program requires the MERG(E)ing of listings 2.1 ('starf), 2.2 (two
functions FN X and FN Y), 2.3 ('setorigin'), 2.4 ('moveto') and 3.3 ('clip' and
'lineto'). This combination of routines will be called 'lib 1" and it was designed
for drawing line figures on the television screen .
To 'lib 1' must be added listings 3.4 ('angle'), 8.1 ('mult3' and 'idR3 '), 8.2
('tran3'), 8.3 ('scale3'), 8.4 ('rot3'), 9.1 ('100k3') and 9.2 ('main program').
Introduction 3
Routines, which when combined we call 'lib3', are used for transforming and
observing objects in three-dimensional space.
We need also listing 10.3 ('revbod ') as well as the 'scene3' routine given in
listing I.l below.
Listing I.l
6000 REM scene3/fLy ing saucer
6010 DIM X(12): DIM Y(12)
6020 DIM S(6): DIM T(6)
6030 DIM A(4,4): DIMB(4,4): DIM R(4,4)
604l1l DATA 0,3,3,2,5,1, 5,0, 4,-1, 0,-3
6050 RESTORE scene3
6060 LET revbod = 6500
6069 REM cr ea t e object.
6070 LET NUMV = 5
6080 INPUT "NUfotlER OF HORIZONTAL LINES",NUMH
6090 INPUT "ANGLE PHI ";PHI
6100 FOR I = 1 TO NUMV + 1: READ SO), TO): NEXT I
6109 REM position the observer.
6110 GO SUB idR3: GO SUB Look3
6129 REM draw object.
6120 GO SUB revbod
6130 RETURN
Throughout the course of this book we will be assuming that the reader is reason-
ably familiar with the BASIC programming language on the ZX Spectrum. In
this chapter, however, we shall be looking at some of the BASIC commands -
those concerned wholly or partly with graphics. With a seriesof example programs
and simple exercises we shall examine and explore the Spectrum's capabilities.
In the chapters that follow we shall use this knowledge to develop a sound
understanding, both practical and mathematical, of computer graphics.
Initially we shall consider the hardware and software facilities available for
producing pictures. All microcomputers that produce television pictures generate
their graphical display using RASTER SCAN technology. This is also true of
most of the newer commercial mini and main-frame computers. An area of memory
is reserved to hold the display information for the screen and this is examined,
bit by bit , as the electron beam sweeps across the raster screen. The display is
composed of points, each of which is represented by a single bit (a binary on/off
switch) in the memory. In the simplest case the beam is switched on for a short
period each time a binary on is found , thus producing a point of light on the
screen .
On the Spectrum we are given two commands ; these directly control the way
that the points are displayed. This affects the picture, which is made up of INK
dots (binary ons) on a PAPER background (binary offs). The commands, named
PAPER and INK (naturally), are called by using the name followed by a number
N (0 ~N ~9) .
PAPER N sets the background colour of the picture. After this statement is
executed, all newly generated binary offs in the memory will be displayed in
colour N (that is, until another PAPER command is executed) .
INK N sets the points of light corresponding to binary ons to colour N in a
similar way .
The number N, when in the range 0 to 7, represents the colour printed above
the corresponding numeric key on the keyboard. IfN is 8, then the colour
6 Advanced Graphics with the Sinclair ZX Spectrum
Display File
Listing 1.1
10 LET CORNER = 16384
20 l.ET VALUE = 137
30 POKE CORNER,VALUE
40 STOP
This program uses POKE to store a VALUE (entered as a decimal) in the first
location of the display me. This location holds the information for the top left-
hand CORNER of the screen. Since each location, or byte, contains eight binary
bits, the first eight points on the display are affected. These change to show a
pattern equivalent to the binary representation of the VALUE : in this case
10001001.
Exercise 1.1
(i) Experiment with different VALUEs and change the program either to ,
(a) use BIN (binary) representation for VALUE, or to
(b) use a FOR. . .NEXT loop to change VALUE.
(ii) Use the PAPER and INK commands to change the background and fore-
ground colours and then re-run the program to see what difference this makes.
BORDER
When the PAPER colour is changed it soon becomes obvious that we cannot
write on the whole of the screen. A BORDER is left around the edge of the
PAPER to avoid the distortion at the edge of the screen suffered by all television
displays. The colour of this BORDER can be changed, in a similar way to the
PAPER and INK colours , by the command
BORDERN
Character Blocks
128 64 32 16 8 4 2
00000000 = o
01100010 = 64 + 32 +2 = 98
10010100 = 128 + 16 +4 =148
10001000 = 128 +8 = 136
10001000 = 128 +8 = 136
10001000 = 128 +8 = 136
10010100 = 128 + 16 +4 = 148
01100010 = 64 + 32 +2 = 98
Figure 1.1
This is the way in which characters are defined (and redefined) on the
Spectrum, but we shall leave further investigation of this until chapter 5. Never-
theless it does illustrate that a picture, even as small as this, takes time to prepare
and requires a comparatively complicated program to produce the display.
Listing 1.2
10 LET CORNER = 16384
20 LET LINE = 256
30 DATA 0,98,148,136,136,136,148,98
40 FOR I = 0 TO 7
50 LET MEMORY = CORNER + I*LINE
60 READ VALUE
70 POKE MEMORY,VALUE
80 NEXT I
90 STOP
We have seen how the screen display can be changed by storing different values
in the display file . But there are over six thousand locations in the display file
and changing each of these individually would be quite tedious. We obviously
need a more effective method of changing the display .
BASIC provides us with graphics commands to deal with this problem, the
simplest of which are PLOT and DRAW. All the graphics commands treat the
display as a grid of 256 points horizontally by 176 points vertically (45056 in
total). These points are known as pixels and are individually identified by a pair
8 AdvancedGraphics with the Sinclair ZX Spectrum
Listing 1.3
10 PLOT 0,175
20 PLOT 255,175
30 PLOT 255,0
4(J PLOT 0,0
50 IF INKEYS 0'000 THEN GO TO 50
60 IF INKEYS = .... THEil GO TO 60
70 DRAW 0,17 5
80 DRAW 255,0
90 DRAW 0,-175
100 DRAw -255,0
110 STOP
This program first PLOTs points at the corners of the PAPER; it then waits
until a key is pressed before joining them up by DRAWing lines around the
boundary of the PAPER. On comparing the PLOT and DRAW commands we see
that there is an important difference in the way they work: the PLOT command
uses the absolute pixel coordinates, whereas the DRAW command uses the
relative positions of the points. This means that , in order to draw a line segment
between two pixel points on the screen , it is first necessary to use PLOT to
move the graphics pen to the point at one end of a line segment, then work out
the position of the second end point relative to the first, before finally the line
may be DRAWn. Note that in listing 1.3 all the points are decided before the
program is run. In general, points are more likely to be INPUT, READ or
calculated while the program is running .
Exercise 1.2
Write a program that calculates the position oflines to draw a grid. DRAW them
using two FOR. ..NEXT loops (one for horizontal lines , the other for vertical
lines).
Graphics Operations of the ZX Spectrum 9
Exercise 1.3
Write a program that accepts N pairs of pixel coordinates as INPUT from the
keyboard, and then DRAWs an irregular polygon of N sides by joining the points
in order. (This requires some careful thought since the first point must be joined
to the last.)
So far we have not discussed the most obvious method of changing the display,
namely using the PRINT and LIST commands. This is because these commands
use character-size blocks and are designed primarily for use with low-resolution
graphics. This topic will be dealt with in chapter 5 but, since the Spectrum allows
high-resolution and low-resolution graphics to be freely intermixed, we give a
small example here . Suppose we add the line
5 LIST
to the start of the program for exercise 1.2, and then set the program to draw a
grid of 32 vertical lines and 22 horizontal lines. We get a display similar to figure
1.2, which shows the size and position of the character blocks.
. . '
,"' 0,
-tr , Ii' I
.' - :t'3(::)1
='::'l
~::'l
::"
::"
-::'l
" ::" ")1::>, I ='55 lZI
~~~~-t;;l~~.f-+-H-t-H-++~*~~~ -_. -
~
~'"
"
t31211 '
:: __ fi.
k
-
'if - - , .C
"
...
[.;. -- = --
Figure 1.2
smaller
length 4 n - 1 , which we number I to 16 as in figure 1.3. Four of these
squares, numbers 2, 8, 9 and 15, are rearrang ed to produce figure 1.4.
13 14- 1S 16
9 10 11 12
5 6 7 8
1 2 3 4
Figure 1.3
1.5
9 5 6 7
1 3 4
Figure 1.4
squares ,
Each of the squares in the pattern is now split up into 16 even smaller
process until
in the same way, and these are similarly rearranged. We repeat this
consists
we have squares with sides of length I. The resulting fractal pattern
in
entirely of unit squares, which we can PLOT as single pixels. The 3program
4 thus in the
listing 1.4 starts from a square with sides of length 64, which is ;
other. The
program there must be three FOR... NEXT loops nested inside each
final picture produced is shown in figure 1.5.
Graphics Operations of the ZX Spectrum 11
Listing 1.4
10 DIM X(16): DIM Y(16)
20 FOR 1 = 1 TO 4
30 FOR J = 1 TO 4
4~ LET K = 4*1 + J - 4
5~ LET X(K) = J - 3: LET Y(K)=I - 3
60 NEXT J: NEXT 1
70 LET X(2) = 0: LET Y(2) =-3
80 LET X(8) = 2: LET Y(8) = 0
90 LET X(9) = -3: LET Y(9) =-1
100 LET X(15) = -1: LET Y(15) = 2
110 FOR 1 = 1 TO 16
120 FO R J = 1 TO 16
130 FOR K = 1 TO 16
1~ LET XX = 16*X(I) + 4*X(J) + X(K)
150 LET YY = 16*Y(I) + 4*Y(J) + Y(K)
160 PLOT 128+XX,88+YY
170 NEXT K: NEXT J: NEXT 1
180 STOP
Figure 1.5
We shall now consider the options that affect the way in which lines and points
are placed on the screen. There are two commands, and to use them we must
enter the command name followed by a number. The number is I to turn the
effect on, and 0 to turn it off again.
INVERSE: while this effect is on, all lines or points will be draw in the
background (PAPER) colour . That is, the binary switches will be turned off
instead of on .
OVER: while this effect is on , any pixel affected by a graphics command will
be flipped to its opposite state . Any pixel of INK is changed to the PAPER
colour , and vice versa. That is, the binary switch for the pixel is flipped over to
the other position .
Using these commands we can produce programs that generate seemingly
complex patterns and rapidly changing displays. Listing 1.5 gives a program that
combines two methods of creating complicated patterns from very simple
instructions.
12 Advanced Graphics with the Sinclair ZX Spectrum
Listing 1.5
10 OVER 1
20 LET LINES = 40fl
30 =
LET A 0 : LET ANGLE = 2*PI/LINES
40 FOR I = 1 TO LINES
50 =
LET X 85*COS A
60 =
LET Y 85*SIN A
70 PLOT 128,88
80 DRAW X, Y
90 LET A = A + ANGLE
100 NEXT I
110 OVER 0
120 STOP
Figure 1.6
Exercise 1.4
Alter listing 1.5 to INPUT the value of LINES and also to INPUT a string variable,
indicating whether or not the OVER option is to be used . Use this program to
explore the parts played by OVER and by the steps on adjacent lines as LINES
is varied.
Graphics Operations of the ZX Spectrum 13
Listing 1.6
10 OVER 1
20 LET X = !~T (RND*256 )
30 LET Y = HIT (RND*176)
~ PLOT X, Y
50 BEEP 0.0S,(X - Y)/10
60 GO TO 20
Exercise 1.5
Alter listing 1.6 to DRAW lines, either between the random points as they are
generated, or from the centre of the screen (128, 88) to each point.
In the above exercise we saw that the OVER option ensured that the display
changed with each command, even if the same command was repeated, for
example , by DRAWing the same point, or line. The OVER option may be used
in th is way to display an object briefly - by drawing it twice, once to put it on
the screen and again to take it off. Listing 1.7 moves a point around the screen
by PLOTting it at its new position and immediately PLOTting its last position
again to remove the old point.
Listing 1. 7
We can extend this program to allow keyboard control of the moving point
(listing 1.8). The lower case letters about " f" enable the point to move in eight
separate directions under our control. If a "p " is typed then the point leaves a
trailing line that shows its past movements : if a " q" is typed then the point
ceases to leave a trail.
14 Advanced Graphics with the Sinclair ZX Spectrnm
Listing 1.8
10 OVER 1
20 LET X = 0: LET Y = 0
30 PLOT X,Y
40 LET OLDX =X: LET OLDY =Y
50 LET XADD =0: LET YADD =0
60 LET AS =INKEYS: I F AS = .... THEN GO TO 60
n: IF AS = "p " THEN OVER 0
80 IF AS ="q" THEN OVER 1
90 I F (AS ="e" OR AS =
"d" OR AS =
"c") AND X > 0 THEN LET XADD = -1
100 IF (AS ="c" OR AS "v" = OR AS =
"b") AND Y > 0 THEN LET YADD = -1
110 IF (AS =" t " OR AS =
"9" OR AS =
"b") AND X < 255 THEN LET XADD =1
120 IF (AS ="e " OR AS "r"= OR AS =
"t") AND Y < 175 THEN LET YADD = 1
130 IF XADD =0 AND YADD 0 = THEN GO TO 60
140 LET X =X + XADD: LET Y = Y + YADD
150 PLOT X, Y
160 PLOT OLDX,OLDY
170 GO TO 40
Listing 1.9
10 LET I =0: INK 9
20 LET UP = 175: LET ACROSS 255 =
30 LET X = 0: LET Y =
0
40 L ET DIF = 1
50 INVERSE I
60 PLOT X, Y
70 DRAW 0,UP: DRAW ACROSS,0
80 PLOT X, Y
90 DRAW ACROSS,0: DRAW 0,UP
100 LET X = X + DIF: LET Y =
Y + DIF
110 LET UP = UP - 2 * DIF
120 LET ACROSS = ACROSS - 2 * DIF
130 IF UP < 0 OR UP =175 THEN LET DIF = -DIF: LET I = 1 - I
140 IF UP = 175 THEN PAPER RND*7: CLS
150 GO TO 50
Exercise 1.6
Draw a solid square composed of 40 by 40 pixels. Move this area about the
screen under keyboard control. Note that you need change only the edges of the
square.
Graphics Operations of the ZX Spectrum 15
Having seen what is possible in black and white, we shall now turn our attention
to colour. The Spectrum can have all the colours on the screen at once, but
inside each character block there can be only two colours, PAPER and INK.
These colours may be BRIGHT and/or FLASHing; also, special effects like
OVER and INVERSE can be turned on or off in the same way.
FLASH: when FLASH is set for a given block, the colours within that block
will alternate between INK on PAPER and PAPER on INK.
BRIGHT: blocks with BRIGHT set will show both PAPER and INK at
increased BRIGHTness. This has the effect of making non-BRIGHT blocks look
darker.
FLASH and BRIGHT can also be set to 8, so that the pre-existing setting for
a block is unchanged by PRINT.
Attributes
The current combinations of FLASH, BRIGHT, PAPER and INK colours for
each character block are stored in memory in the attribute file . This contains
one location for each of the character blocks; it is located in memory immediately
after the display file, and starts at location 22528. We can use listing 1.10, a
modified version of listing 1.1, to alter these values directly, as we did with the
display file.
Listing 1.10
10 LET CORNER = 22528
20 INPUT "VALUE = BIN "; LINE VS
30 LET VALUE = VAL ("BIN " + VS>
4lil PRINT AT 0,0;"*"
50 POKE CORNER,VALUE
60 GO TO 20
Exercise 1.7
Use the program from listing 1.10 to alter individual bits within the VALUE
stored in the first location of the attribute file.
This VALUE affects the whole of the first character block by indicating for
points in that block whether FLASH and BRIGHT are on or off, and what
PAPER and INK colours are used. These pieces of information make up a
BINary number in the following manner
Thus we can calculate the meaning of a value in the attribute me in the way
shown in figure 1.7.
16 Advanced Graphics with the Sinclair ZX Spectrum
Figure 1. 7
Listing 1.11
10 FLASH INT (RND*2)
20 BR IG HT INT (RND*2)
30 PAPER INT (RND*8)
40 INK INT (RND*8)
50 LET ROW = INT (RND*22): LET COL = INT (RND*32)
611' PR INT AT ROW, COL; "*"
70 BEEP 0.1l'2,RND*40
811' GO TO 10
Exercise 1.8
Modify the above program so that it changes just one character block to a
random attribute setting, and then calculates and displays the FLASH, BRIGHT,
PAPER and INK settings from the ATTR function .
For convenience we can use table 1.1 to convert between attribute file value
and the attribute settings.
Table 1.1 Attribute Conversion
Black 0 1 2 3 4 5 6 7 NORMAL
64 65 66 67 68 69 70 71 BRIGHT
128 129 130 131 132 133 134 135 FLASH
192 193 194 195 196 197 198 199 BRIGHT
+ FLASH
Graphics Operations of the ZX Spectrum 17
Blue 8 9 10 11 12 13 14 15 NORMAL
72 73 74 75 76 77 78 79 BRIGHT
136 137 138 139 140 141 142 143 FLASH
200 201 202 203 204 205 206 207 BRIGHT
+ FLASH
Red 16 17 18 19 20 21 22 23 NORMAL
80 81 82 83 84 85 86 87 BRIGHT
144 145 146 147 148 149 150 151 FLASH
208 209 210 211 212 213 214 215 BRIGHT
+ FLASH
Magenta 24 25 26 27 28 29 30 31 NORMAL
88 89 90 91 92 93 94 95 BRIGHT
152 153 154 155 156 157 158 159 FLASH
216 217 218 219 220 221 222 223 BRIGHT
+ FLASH
Green 32 33 34 35 36 37 38 39 NORMAL
96 97 98 99 100 101 102 103 BRIGHT
160 161 162 163 164 165 166 167 FLASH
224 225 226 227 228 229 230 231 BRIGHT
+ FLASH
Cyan 40 41 42 43 44 45 46 47 NORMAL
104 105 106 107 108 109 110 111 BRIGHT
168 169 170 171 172 173 174 175 FLASH
232 233 234 235 236 237 238 239 BRIGHT
+ FLASH
Yellow 49 49 50 51 52 53 54 55 NORMAL
112 113 114 115 116 117 118 119 BRIGHT
176 177 178 179 180 181 182 183 FLASH
240 241 242 243 244 245 246 247 BRIGHT
+ FLASH
White 56 57 58 59 60 61 62 63 NORMAL
120 121 122 123 124 125 126 127 BRIGHT
184 185 186 187 188 189 190 191 FLASH
248 249 250 251 252 253 254 255 BRIGHT
+ FLASH
18 Advanced Graphics with the Sinclair ZX Spectrum
We can think of each pixel on a colour television screen as three dots of light
packed closely together at the vertices of an equilateral triangle . For each pixel
there is one red, one blue and one green dot, and the attribute-file locations are
used to control the illumination of the three different colours. The display file
will indicate that a given pixel is to be plotted in a particular INK colour. The
lowest three bits (bits 0 to 2) of the attribute value for the block containing that
pixel are used to decide whether the green, red and blue dots of that pixel are
on or off. Our eyes contain only three types of colour sensor (green, red and
blue) . Our brain takes the signals from the three dots and combines them into a
single dot of composite colour. So if the last three bits of the attribute are 111,
equivalent to colour 7, we get green, plus red, plus blue. This corresponds to
white light or white INK . The other colour codes, when written in binary form,
can be translated in this way (see figure 1.8).
Black 0 000
Blue 1 001 Blue
Red 2 010 Red
Magenta 3 011 Red + Blue
Green 4 100 Green
Cyan 5 101 Green + Blue
Yellow 6 110 Green + Red
White 7 111 Green + Red + Blue
Figure 1.8
Exercise 1.9
Experiment with different colours using the programs in this chapter. Certain
colour combinations can be just too much for a normal television set to cope
with. Unless you are using an expensive monitor instead of a television screen, a
combination of clashing colours for the program in listing 1.5 should produce a
rather interesting effect of waves washing across the screen.
Graphics Operations of the ZX Spectrum 19
Simple Animation
We can produce more animated effects in low resolution by using colours and
FLASH . Listing 1.12 shows some interesting techniques of colour animation.
The first part of the program is particularly useful because the display needs no
maintenance once set up . The boundary of the picture is a sequence of blocks
composed of alternative blocks of FLASHing red PAPER and cyan INK, and
FLASHing cyan PAPER and red INK. On seeing this our brains are tricked into
believing that the red and cyan colours are moving around the boundary
sequence .
Listing 1.12
Exercise 1.10
Write low-resolution colour versions of the bouncing point program and the
other animation programs. In your programs move character blocks instead of
pixels around the screen .
The Spectrum has two further built-in high-resolution graphics commands that
we have not yet examined: the CIRCLE command and the DRAWcommand for
curved lines.
CIRCLE X, Y, R draws a CIRCLE of radium R pixels centred on pixel (X,
Y) . It is important to remember that after obeying this command our graphics
pen is left at a pixel on the right-hand side of the CIRCLE, just below the centre .
20 Advanced Graphics with the Sinclair ZX Spectrum
Figure 1.9
Figure 1.10
Graphics Operations of the ZX Spectrum 21
Listing 1.13
10 CIRCLE 128,88,80: LET h = 12
20 LET A ~ 0: LET ADIF = 2*PI/N
30 FOR I = 1 TO N
40 PLOT 128,88
50 LET X = 40*COS A : LET Y = 40*SIN A
60 ORAI' X, Y, - P! : DRA'" X,Y,PI
70 LET A =A + AOIF
80 NE XT I
Exercise 1.11
Figure 1.10 shows the traditional Celtic pattern known as a triskele. Write a
program to draw this type of pattern .
The use of different INK colours for high-resolution graphics can cause problems
and produce results that are calculable but usually unforeseen. Run the program
in listing 1.14 . It will show just how easily things can go wrong when more than
two colours are used without careful planning.
Listing 1.14
10 PAPER 5: H:K 7 : CLS
20 FOR ! = 0 TO 17 5 STEP 8
30 FLOT ~,I : OR AIi 255,0
40 NE XT I
50 INK ~
60 CIRCLE 128,88,80
70 STOP
This problem can be used to our advantage. We can produce rapidly changing,
and complicated, low-resolution colour displays. Initially we PRINT solid INK
blocks at specified positions on the screen. Any line drawn subsequently will
change the colour of the low-resolution blocks through which it passes. The
impressive speed of this technique can be seen by running the program given in
listing 1.15 .
Listing 1.15
10 INK 7: CLS
20 FOR! = 1 TO 704: FRINT "I " ; : NEXT I
30 LET Dr ST = 80: LET I = 0 : LET D = e
40 INK I
50 FLOT 120,86 + OIST: ORA'" OIST,-DIST: DRAII - CI ST, - DI ST
60 FLOT 127,86 - DIST: DRAII -CIST,DIST: DRAW DrST,DIST
70 LET DIST = DIST - D: IF DIST = 0 OR DIST = 80 THEN LET D = - D
80 LET I = 1+ 1: IF I = 8 THEN LET I = 0
90 GO TO 40
22 Advanced Graphics with the Sinclair ZX Spectrum
A Simple Game
We now include a small game program (listing 1.16) as a final example of the use
of the techniques discussed in this chapter. A worm can move in character block
steps about the screen, horizontally or vertically , under control of the keyboard .
The aim of the game is for the worm to eat the money (or target). The worm gets
longer whenever it eats the target. If at any time the head of the worm runs
headlong into the boundary , or into its own body, then the worm dies. After ten
successful meals the worm returns to its original size, with a fanfare . The game
then continues.
This game was developed using modular , structured methods preferred by
programmers. These methods help to produce quickly a working and understand-
able program. Put simply, we must approach the program as a series of small
tasks that build up block by block into the completed program . For this game
these tasks were tentatively defined as
A. Initialise variables
B. Set up board
C. Control game
D. Update and print score
From this overview of the problems we can set about solving each problem or , if
necessary, split them into yet smaller, more manageable problems. For example ,
task C above could be split into
I. Generate target
2. Use keyboard to change direction of worm
3. Move worm
a. Draw worm
b. Worm hits boundary or itself, and dies
c. Worm eats money and grows
d. Fanfare
No specific order is implied in this breakdown; for example, you may find that
you want to regenerate the target from inside the fanfare section of program.
These headings are simply a list of tasks that reflect the problems that come to
mind when attempting the solution of a larger problem.
Examine the game below and try to identify which tasks are carried out,
where, in what order, and which have been further subdivided. (Throughout this
book the variable names in lower case will refer to line numbers at the start of
subroutines. This helps to make the program more readable, gives a clear picture
of the algorithm, and hence is good general practice.)
Graphics Operations of the ZX Spectrum 23
Note the use of logical expressions (for example, IF DEAD THEN. ..): see
chapter 13 of the Spectrum BASIC Handbook (Vickers, 1982). Also note the
use of ATTR and SCREEN$ to detect collisions, both by the colour of character
blocks and by their contents. Figure 1.11 shows a typical state of the game.
EVEL 3 ,",ORHS 3
Figure 1.11
Listing 1.16
4060 IF A$ =
"k" AND RMOVE THEN LET RMOVE =0 : L ET CMOV E =1
: L ET H$ =
" >": RETURN
41H0 RETURN
Exercise 1.12
As a final mini-proje ct for this chapter , write a squash game or ping-pong video
game (o r both! ) using low-resolution colour graphics. The ball can be a pixel or
character block , and the bat (s) should be controlled from the keyboard like the
worm in the above program . You will find it useful to tum some of the program
sections from this chapter into subroutines.
In this chapter we have restricted ourselves to using the screen as a fixed piece
of PAPER for patterns and games. To step up from pixel graphics to drawing
pictures of real objects , we need commands that will relate the real world to our
PAPER. In the following chapters we shall explore and develop the techniques
needed to draw these real graphics pictures .
Complete Programs
In general, computer graphics deals with points, lines, areas and volumes in con-
tinuous two-dimensional and three-dimensional Euclidean space. Pixel graphics
is very limited . The definition of objects that use only discrete pairs of integers
is very rare in most practical applications. We therefore need to consider ways of
plotting views of objects on a graphics screen, where positions are measured in
real units : inches, miles or even light-years! Therefore we consider the relation-
ship between two-dimensional real space and screen pixels. Before we can
attempt this step, however, we must first discuss ways of representing two-
dimensional space using Cartesian coordinate geometry.
We can imagine two-dimensional space as the plane of this page extending to
infinity in all directions. Our description of the coordinate geometry starts by
arbitrarily choosing a fixed point in this space, which we call the coordinate
origin. Through the origin we draw a line that stretches to infinity in both
directions; this is the x-axis . The normal convention is to place this line left to
right on the page (the horizontal). Another two-way infinite line, the y-axis, is
28 Advanced Graphics with the Sinclair ZX Spectrum
+p == (X, Y)
~
+x
- 3 1 2 3
Figure 2.1
Taking any point p in space we can now uniquely fix its position by specify-
ing its coordinates (figure 2.1) . The x-eoordinate, X say, is that distance along the
x-axis (positive to the right of the axis and negative to the left) at which a line
perpendicular to the x -axis passing through p cuts the x-axis, The y-coordinate,
Y say, is correspondingly defined using the y -axis. These two values, called a co-
ordinate pair or two -dimensional vector, are normally written in brackets thus :
(X, V). Note that the x-coordinate comes before the y-coordinate. We shall
usually refer to the pair as a vector - the dimension (in this case two) will be
understood from the context in which we use the term . A vector, as well as
defining a point (X, Y) in two-dimensional space, can also be used to specify a
direction, namely the direction that is parallel to the line joining the origin to
the point (X, Y) - but more of this (and other objects such as lines, curves and
polygonal areas) in chapter 3.
We are now in a position to devise means (the above-mentioned primitive
routines) for mapping such geometrical concepts on to the two-dimensional dis-
crete rectangular matrix of pixels that form the graphics frame .
From Real Coordinates to Pixels 29
Listing 2.1
This routine may be extended should we need colour; it must have two extra
parameters COLPAPand COLINK (integers between 0 and 7) for the colour of
the paper and ink respectively. The following extra statement should be added
Listing 2.2
9650 DEF FN X<Z) INT «XORIG + Z)*XYSCALE + 0.5)
9660 DEF FN Y(Z) INT «YORIG + Z)*XYSCALE + 0.5)
The next primitive routine (listing 2.3) is 'setorigin'. This enables us to move
the coordinate origin by an amount XMOVE horizontally and YMOVE vertically
(distances in the scale of the coordinate system), consequently adjusting the
(XORIG, YORIG) values. After such a move the plot pen moves to the pixel
equivalent of the new origin.
Listing 2.3
960~ REM setorigin
9601 REM IN : XORIG, YORIG, XMOVE, YMOVE
9602 REM OUT : XORIG, YORIG, XPEN, YPEN
961~ LET XORIG = XORIG + XMOVE: LET YORIG = YORIG + YMOVE
9620 LET XPEN = FN X(~)
963~ LET YPEN = FN y(~)
9640 RETURN
initiate the machine-dependent BASIC pixel instructions for drawing a line (note
that PLOT is absolute and DRAW is relative); however, the 'moveto' routine is
machine-independent. Hence if you wish to implement these routines on a differ-
ent microcomputer you need only alter the 'lineto' routine.
Listing 2.4
950fl REM moveto
9501 REM IN : XPT, YPT
9502 REM OUT : XPEN, YPEN
9510 LET XPEN = FN X(XPT)
9520 LET YPEN = FN Y(YPT)
9530 RETURN
Listing 2.5
940fl REM Li neto
9401 REM IN : XPT, YPT, XPEN, YPEN
9402 REM OUT : XPEN, YPEN
9410 LET NXPEN = FN X(XPT)
9420 LET NYPEN = FN Y(YPT)
9430 PLOT XPEN,YPEN
9440 DRAW NXPEN-XPEN,NYPEN-YPEN
9450 LET XPEN = NXPEN: LET YPEN = NYPEN
9460 RETURN
In all but the most elementary machines, it is possible to set up these plotting
routines or their equivalents (and many more as our knowledge increases) in a
library file or backing store. Then there is no need to retype them explicitly into
each new program. On the Spectrum we can store them as files on audio-cassettes
(and MERGE them if necessary) . On the companion cassette to this book you
will find these routines as part of the 'lib 1' library .
Example 2.1
Identify a rectangle in Cartesian space, 30 units by 20 units, with the graphics
frame of the Spectrum. Then draw a square of side 15 units, centred in the
rectangle (figure 2.2a) .
We centre the square by moving the origin to (15 .0,10.0) and thus define the
corners of the square to be (±7.5, ±7.5). See listing 2.6.
Listing 2.6
10fl REM draw;r,g a square
109 REM setup i dent i f i e r s to graph ics routines.
110 LET start = =
970fl: LET set or i gi n 960fl: LET moveto = 950fl
: LET l i ne t o = 940fl
119 REM define graph ics area.
120 LET ~ORIZ = 30: LET VERT = 20
130 GO SUB sta rt
140 LET XMCVE = HORIZ*0.5: LET YMOVE = VERT*fl.5
150 GO SUB setor igin
159 REM jo in corners of square in order.
160 LET XPT = 7.5: LET YPT = 7.5: GO SUB movetc
32 Advanced Graphics with the Sinclair ZX Spectrum
(a) (b)
Figure 2.2
It is as well to note , at this juncture, that the order in which the points are
joined is critical. For example, if the coordinates of the second and third corners
of the square are interchanged then figure 2.2b will be drawn.
Next we write a primitive routine 'polygon' that uses the Spectrum line-
drawing instruction to draw such figures. The routine is given the NPOL vertices
of the polygon as arrays X and Y (the x-coordinate and y -coordinate). We also
give an example main program calling this routine (listing 2.7) .
Listing 2. 7
Exercise 2.1
If we are using the Spectrum then it is possible to draw pictures in a variety of
colours. But before drawing it is necessary to set the colour using the INK opera-
tion . Write a routine 'setcolour' with one integer parameter COLINK that
achieves this .
Exercise 2.2
In all the plotting routines above, the scale of the mapping (XYSCALE) is fixed
once and for all; and the horizontal and vertical scaling factors are identical.
There is no need to heed this convention : write a routine 'factor' that alters the
horizontal scale by FX and the vertical by FY. Naturally, this implies that we
now have to define two separate scales (XSCALE and YSCALE, say); and also,
of course, the 'start', 'set origin' , 'moveto ' and 'lineto' routines must be altered
(see also chapter 6).
Exercise 2.3
There is no reason for the x -axis and y-axis to be identified with the horizontal
and vertical respectively . In fact they need not even be mutually perpendicular.
Experiment with these ideas, which necessarily involveschanging all the plotting
routines 'start', 'moveto', etc .
Example 2.2
One of the first popular graphics packages was CalComp. This includes a number
of routines to draw axes and scales for the construction of graphs, and many
other useful subroutines. They are all based on a line-drawing routine named
'plot' (not to be confused with the Spectrum PLOT), which is central to the
package ; 'plot' has three parameters, two reals XPT and YPT, the coordinates of
a point in space, and the movement information MOVE,an integer whose value
is set to ±2 or ±3. This one routine may be used to replace all three of our
routines 'setorigin', 'moveto' and 'lineto'. If MOVEis negative, then a new co-
ordinate origin is fixed at the point (XPT, YPT) of the old coordinate system -
equivalent to 'setorigin'. When the absolute value of MOVEis 3, then the plot
head is moved without drawing a line - equivalent to 'moveto' : when it is 2,
then a line is drawn - equivalent to 'lineto'.
Naturally, even if we do not wish to implement the complete CalComp pack.
age, we can still implement the 'plot' routine in place of 'setorigin', 'moveto' and
'lineto'; and use it instead, in conjunction with the remaining routines mentioned
in this chapter - see listing 2.8 .
34 Advanced Graphics with the Sinclair ZX Spectrum
Listing 2.8
980~ REM pLot I CaLComp
9801 REM IN : XPT, YPT, XPEN, YPEN, XORIG, YORIG, MODE
9802 REM OUT : XPEN, YPEh, XORIG, YORIG
9810 LET NXPEN = FN X(XPT)
9820 LET NYPEN = FN Y(YPT)
983~ IF ASS (MODE) = 2 THEN PLOT XPEN,YPEN: DRAW NXPEN-XPEN,NYPEN-YPEN
9840 LET XPEN = NXPEN: LET YPEN = NYPEN
9850 IF MODE < ~ THEN LET XORIG = XOR IG + XPT: LET YORIG = YORIG + YPT
9860 RETURN
To demonstrate the use of these plotting routines we shall draw some simple
patterns. There are those who think that the construction of patterns is a frivol-
ous waste of time . Nevertheless, we consider it a very useful first stage in under-
standing the techniques of computer graphics. Often, patterns of an apparently
sophisticated design are the result of very simple programs. Quickly producing
such graphical output is an immediate boost to morale , and gives a lot of con-
fidence to the beginner. Furthermore, new designs are always in demand:
geometrical art is used for the covers of books and pamphlets and in advertising
literature. It can do no harm to initiate artistic ideas that will be of great use later
when we study the pictorial display of data. Patterns are also an ideal way of
introducing some of the basic concepts of computer graphics in a very palatable
way. Take the next example, which looks at the important role of trigonometric
functions (sine and cosine), and of angular measurement in radians! Remember
that rr radians is the same angular measure as 180 degrees.
Figure 2.3
From Real Coordinates to Pixels 35
Example 2.3
Figure 2.3, a very popular design, is constructed by joining each vertex of a
regular N-sided polygon (an N-gon) to every other vertex. N is not greater than
30 .
We set the origin at the centre of the design, and all the vertices at a unit
distance from the centre: the sizes of the HORIZ and VERT (3, 2.1) , are chosen
so that the design fits neatly on the screen. If one of these vertices lies on the
positive x-axis (the horizontal), then the N vertices are all of the form (COS
(ALPHA), SIN(ALPHA)), where ALPHA is an angle 21fI/N and I is chosen from
1,2, . .., or N. Here for the first time we see point coordinates being calculated
by the program, not explicitly typed in, as in listing 2.6. Furthermore, since the
program uses these values over and over again, it is sensible to store them in
arrays and access them when required by specifying the correct array index.
Note that in listing 2.9, if 1 0:;;; I 0:;;; J 0:;;; N, then the J th point is not joined to the
I th point ; the line will have already been drawn in the opposite direction .
Listing 2.9
There are two immediate observations to be made from this very simple
example. The first concerns resolution. Because the graphics frame is a discrete
matrix, then straight lines must be approximated by a sequence of pixels. Un-
fortunately, the resolution of the Spectrum, like most microcomputer graphics
systems, is low (that is, NXPIX and NYPIX are the order of hundreds) so the
lines appear jagged; even in higher-resolution devices (like microfilm plotters)
the same is true, but the sizes involved are so small that the jaggedness goes
unnoticed.
36 Advanced Graphics with the Sinclair ZX Spectrum
The second observation is that as N increases in listing 2.9, the outline of the
figure (the N·gon) approximates closely to a circle . Therefore we can use this
idea to write a routine 'circle!' (listing 2.10a), which draws a circle with radius
R about the centre (XCENT, YCENT) to give a picture similar to figure 2.4.
Note that we are using angles measured in radians; that is, we are incrementing
by 3/(R*XYSCALE) each time through the loop - a value that depends on the
radius and produces a reasonable circle without waste of effort. Note also that
since the vertices of the N·gon are only needed once, we do not store their values
but calculate them as required . Again, the limitation in resolution of the screen is
apparent on the circumference of the circle.
Figure 2.4
Listing 2.10a
3~~ REM ci rc Le1
301 REM IN : XCENT, YCENT, R, XYSCALE
31~ LET XMOVE = XCENT: LET YMOVE = YCENT : GO SUB setorig in
320 LET AOIF = 3/(R*XYSCALE)
33~ LET XPT = R: LET YPT = 0: GO SUB moveto
339 REM caLculate and join points (XPT,YPT) around the circLe.
340 FOR A = AOIF TO 2*PI STEP AOIF
350 LET XPT = R*COS A: LET YPT = R*SIN A: GO SUB Lineto
360 NEXT A
370 RETURN
Listing 2.1 Ob
400 REM circLe2
401 REM IN : XCENT, YCENT, R, XYSCALE
410 CIRCLE FN X(X CENT) , FN Y(YCENT),R*XYSCALE
420 RETURN
We saw that the Spectrum has a BASIC function CIRCLE that enables us to
draw a circle. So we can incorporate this in a primitive routine 'circle2' (listing
2.l0b) for drawing a circle, one that is necessarily more efficient than 'circle! ' .
Whenever we use such routines, we must be aware of any side-effects pro-
duced ; for example, has the origin or plot head been moved by the routine? For
From Real Coordinates to Pixels 37
example, listing 2.1Oa changes the position of both the origin and plot pen,
whereas listing 2.1Ob does not. It would therefore be sensible to add the follow-
ing line to the 'circle 1' routine
Exercise 2.4
Write a routine to draw an ellipse of major axis A units (horizontal) and minor
axis B units (vertical). Note that a typical point on this ellipse has coordinates
(A cos ex, B sin ex) where 0 < ex < 27T. However, it must be remembered that, un-
like the circle, ex is not the angle made by the radius through the point with the
positive x-axis, It is simply a descriptive parameter.
Incorporate this routine in a program that draws a diagram similar to figure
2.5 . Here are two things to note: (1) there is no need for A to be greater than B;
and (2) observe the optical illusion of the two apparent white diagonal lines.
Another illusion can be seen in figure 23 - dark circles radiating out from the
centre of the pattern. The study of optical illusions is fascinating (see Tolansky,
1964) and it is a never-ending source of ideas for patterns. This exercise was
introduced because it leads the way to the general technique of drawing curves
(see chapters 3 and 6) .
. .---------
_.- ,
-,
.' -,~ - -
i .- - - ~ - ,
\. . '. .
./
I
I
I
I
/
.:
Figure 2.5
Example 2.4
An extension of this idea, the natural next step , is the construction of a spiral.
Again the general form of the curve about the origin is (R cos ex, R sin ex) but now
ex varies between angles (3 to (3 + 2N7T, where (3 (the parameter BETA) is the initial
angle that the normal to the spiral makes with the positive x-axis , and N is the
38 Advanced Graphics with the Sinclair ZX Spectrum
number of turns in the spiral. The radius R is no longer a constant value, but
varies with the value of a : if RMAX is the outer radius of the spiral then R is
given by the formula
R = RMAX(a - (3)/2Nrr
Note that this routine, which centres the spiral at (XCENT, YCENT), causes no
side-effects because we reset the origin back to its original position before
leaving the routine.
Listing 2.11 a
Listing 2.11 b
Exercise 2.5
Listing 2.11a produces a diagram similar to figure 2.6a (with XCENT = 0,
YCENT = 0, N = 4, BETA = 1 and RMAX = 3). What happens if you set RMAX
to -3? Use the routine in a program that generates figure 2.6c. Again note the
optical illusion when the observer's head is moved in a circle in front of the
diagram, keeping the horizontal (and hence also the vertical) direction parallel
with the original. The spirals appear to rotate about the centre!
From Real Coordinates to Pixels 39
Example 2.5
Spirals have been used in art and design for thousands of years ; however, most of
the ancient spirals were not true spirals but consisted of sequences of semicircles
(see Bain, 1972). Listing 2.11 b (a routine with input parameters RMAX, Nand
SIGN - the orientation value being ±1) enables us to draw such semicircular
spirals using the DRAW option, and so it is much more efficient than the accurate
method of listing 2.11a (for example, figure 2.6b).
-::~::~:::~
\\ ,/'~;:1 \1;~~
,~ .J""'))
."
... "'... --_· ...···· ..·/ / l l
" -,, . . ~ _ _.. - .... .l
\\.~:::?~~~~:::: :::: ~.,
"I ...
(a) (b)
/;;~-----.:~.
\
.1 \
.~.~
~-...... ~.,
.. ...
--:...- ' :.-'../'./'/
..
..
I; I
::.. 1'\ . . ~---...: - ._..J t .. t .. ...
Exercise 2.6
Follow the logic of listing 2.11b, and extend it so that the normal to the original
curve does not go along the x-axis but makes an angle BETA with it . In the Book
of Kells there are examples of triskeles, which are composed of a set of third-
circles joined in sequence. Experiment in constructing these and any other
variations on this method, such as quarter-circles, etc.
40 Advanced Graphics with the Sinclair ZX Spectrum
Example 2.6
Write a routine (listing 2.12) that draws diagrams similar to figure 2.7 .
Here we introduce the concept of an envelope. Instead of drawing a curve by
a sequence of small line segments (as in the circle of listing 2.9), we devise a
sequence of lines that are tangential to the curve. For example, the figure shows
four rectangular hyperbolae placed in the quarters of the plane.
N points are placed on each of the four arms (of unit length) that divide the
plane into the four quarters. The 4N points are therefore (±I/N, 0.0) and (0.0,
±I/N) where I = 1,2, .. ,N.
Figure 2. 7
Listing 2.12
100 REM exampLe of an enveLope
110 LET start = 9700: LET setorigin = 96e0
: L.ET moveto = 9500 : LET L ineto = 941110
120 LE1 ,",CRIZ = 3: LET VERT = 2.1
130 GO SUB start
140 LET XMOVE = HORIZ*0.5: LET YMOVE = VERT*0.5
1511l GO SUB setorigin
159 REM draw unit axes in graphics area.
160 INPUT "TYPE N "; N
170 LET XPT = =
1: LET YPT Ill: GO SUB moveto
180 LET XPT = -1 : LET YPT = 0: GO SUB Lineto
190 LET XPT = 0: LET YPT = 1: GO SUB Lineto
200 LET XPT = =
0: LET YPT -1: GO SUB l;~eto
208 REM produce N sets each of four po;nt s, one on each axis.
209 REM join the points of each se t in order.
210 FOR I = 1 TO N
220 LET 101 = IIN: LET 102 = (N + 1 - I)/N
230 LET XPT = 101 LET YFT = 0 GO SUB moveto
240 LET XPT = 0 LET YPT = 102 GO SUB l ineto
2511l LET XPT = - 101 LET YPT = 0 GO SUB l i ne t o
260 LET XPT = 0 LET YPT = -102 GO SUB Lineto
270 LET XPT = 101 LET YPT = 0 GO SUB L i ne t o
280 NEXT I
290 STOP
From Real Coordinates to Pixels 41
Exercise 2.7
Generalise this routine so that there is a variable number of arms, M, stretching
out from the origin and dividing the plane into equal segments.
Exercise 2.8
Draw a diagram similar to figure 2.8; the routine will have an integer parameter N.
It will calculate 4 N points {P(I) : I = 1, 2 , . . . , 4 N } around the edges of a square
of unit side, starting at a corner. There is one point at each corner and the points
are placed so that the distance between consecutive points is lIN . Then, pairs of
points are joined according to the following rule : P(I) is joined to P(J) for all
positive I and J less than or equal to 4N, such that J - 1 (subtraction modulo
4N) belongs to the sequence 1, 1 + 2, 1 + 2 + 3, .. . For example , if N is 10, then
P(20) is joined to P(2l), P(23) , P(26), P(30), P(35), P(l), P(8) and P(l6). The
outer square should be drawn , and hence if two points lie on the same side of
the square there is no need to jo in them by a line since it already exists as an
edge of the outer square. For example, P(20) is a corner , so it is on the same
edge as P(l6) and also P(2l), P(23), P(26) and P(30) .
Figure 2.8
Example 2.7
Emulate a Spirograph, in order to produce diagrams similar to figure 2.9.
A Spirograph consists of a cogged disc inside a cogged circle, which is placed
on a piece of paper. Let the outer circle have integer radius A and the disc integer
radius B. The disc is always in contact with the circle . There is a small hole in the
disc at a distance D (also an integer) from the centre of the disc, through which is
placed a sharp pencil point. The disc is moved around the circle in an anti-
clockwise direction, but it must always touch the outer circle; the cogs ensure
42 Advanced Graphics with the Sinclair ZX Spectrum
Figure 2.9
that there is no slipping. The pencil point traces out a pattern, which is complete
when the pencil returns to its original position.
Initially we assume that the centres of the disc and the circle and also the hole
all lie on the positive x-axis, the centre of the circle being the coordinate origin.
In order to emulate the Spirograph we need to specify a general point on the
track of the pencil point. We let a be the angle made with the positive x-axis by
the line joining the origin to the point where the circle and disc touch. The point
of contact is therefore (A cos a, A sin a) and the centre of the disc is ((A - B) cos
a , (A - B) sin a). If we let ~ be the angle that the line joining the hole to the
centre of the disc makes with the x-direction, then the coordinates of the hole are
The point of contact between the disc and circle will have moved through a
distance Aa around the circle, and a distance -BI3 around the disc (the minus
sign is because a and 13 have the opposite orientation). Since there is no slipping,
these distances must be equal, and hence we have the equation 13 = -(A/B)o:.
The pencil returns to its original position when both a and 13 are integer multiples
of 21T. When a = 2N1T, then 13 = -N(A/B)21T: hence the pencil point returns to its
original position for the first time when N(A/B) becomes an integer for the first
time; that is, when N is equal to B divided by the highest common factor of B
and A. The routine 'Euclid' (listing 2.13) uses Euclid's algorithm (see Davenport,
1952) to calculate the highest common factor (integer HCF) of two positive
integers A and B.
This function is used in the routine 'spiro' (listing 2.13), which calculates
the value of N and then varies a (ALPHA) between 0 and 2N1T in steps of 1T/I00 ;
for each a, the value of (3 (BETA) is calculated and then the general track is
drawn. Figure 2.9 was drawn by a call to 'spiro' with A = 12, B = 7 and D = 5.
The size of HORIZ and VERT must be chosen so that the figure fits on the
screen; in this case HORIZ = 30 and VERT = 20.
From Real Coordinates to Pixels 43
Listing 2.13
20 0 REM EueL id
201 REM IN : A, B
202 REM OUT : HCF
21 0 LET I = A: LET HCF= B
220 IF A < B THEN LET I = B: LET HCF =
A
230 LET J = I - INT (I/HCF)*H CF
240 IF J = iii THEN RETURN
250 LET I = HCF: LET HCF = J: GO TO 230
Complete Programs
At this stage we shall group the listings 2.1 ('start'), 2.2 (two functions FN X
and FN V), 2.3 ('setorigin'), 2.4 ('moveto') and 2 .5 ('lineto') under the heading
'lib I ' . Later we shall replace listing 2.5 with listing 3.3 ('clip' and a new version
of 'lineto').
the centre (XCENT, YCENT) and radius R. Choose these values so that the
figure is consistent with your values of HORIZ, VERT, XMOVE and
YMOVE. Try 30 ,20,15 and 10 respectively . As an example call 'circle 1'
with centre (I , -1), radius 8 and 'circle2 ' with centre (I , 2), radius 5.
V. 'lib l ' and your own 'main program' calling listings 2.IIa ('spiral1') and
2.1 lb ('celtic'). Each routine requires the centre (XCENT , YCENT),
maximum radius RMAX and number of turns in the spiral N. Listing 2.11 a
('spirall ') also requires an angle BETA, whereas 2.1 1b ('celtic') needs a
value SIGN, which is ± 1. Choose these values so that the figure is consistent
with your values of HORIZ, VERT, XMOVE and YMOVE (for example ,
30,20,15 and 10). For example , call 'spiralI ' with centre (1, - 1) , RMAX
=8, N =3 and BETA =2, and call 'celtic' with centre (1,2) , RMAX =5,
N =5 and SIGN =-1 . Also try SIGN =+1.
VI. 'lib 1' and listing 2.12 ('envelope') : requires a integer N, 2 ~ N ~ 30 .
VII. 'lib l ' and listing 2.13 ('Euclid' and 'spiro') : requires three integers A, Band
D, where A> B> D. Choose HORIZ, VERT , etc. , so that the diagram fits
on the screen: set XMOVE = HORIbO.5, YMOVE = O.5*VERT (for
'setorigin'), where both HORIZ and VERT are greater than 2*(A - B + D).
3 Two-dimensional Coordinate
Geometry
ay = bx + c
This allows for all possible lines: if the line is vertical, a is 0; (bla) is now the
tangent of the angle that the line makes with the positive x-axis, and the line
cuts the y-axis at (cia) , provided that a is not equal to zero, and the x-axis at
(-clb), provided that b is not equal to zero. The line is parallel to the y-axis if a
is zero, and to the x-axis if b is zero .
We shall frequently use this formulation of a line in the following pages; how-
ever, we now introduce another, possibly more useful, method for defining a line.
Before we can describe this new method we must first define two operations on
vectors (namely , scalar multiple and vector addition) , as well as describe another
required operation - the absolute value of a vector. Suppose we have two
vectors p , =(xl,Yd and p , =(X2'Y2), then
scalarmultiple kp , = (k X Xl ' k X Yl) , we multiply the individual coordinates
by some scalar value (that is, real) k .
46 Advanced Graphics with the Sinclair ZX Spectrum
that is, the vector «(1 - /l) x XI + /l X X2, (1 - /l) X YI + /l x Y2). We place the
/l in brackets after P to show the dependence of the vector on the value of u.
Later when we understand the relationship more fully we shall leave out the (/l).
If 0 < /l < 1, then p(/l) lies on the line somewhere between PI and P2' For any
specified point p(/l) , the value of /l is given by the ratio
See figure 2.1, which shows a line segment between points (-3, -1) == p(O) and
(3, 2)==p(1) : the point (1,1) lies on the line asp(2/3) . Note that (3, 2) is a
distance 3y5 from (-3, -1), whereas (1, 1) is a distance 2Y5 . From now on we
omit the (/l) from the point vector.
Example 3.]
Wecan further illustrate this idea by drawing the pattern shown in figure 3.1. At
first sight it looks complicated, but on closer inspection it is seen to be simply a
square, outside a square, outside a square, etc. The squares are getting successively
smaller and they are rotating through a constant angle. In order to draw the
diagram we need a technique that, when given a general square, draws a smaller
internal square rotated through this fixed angle. Suppose the general square has
four corners {(Xi. Yi) I i = 1, 2, 3, 4} and the ith side of the square is the line
joining (xi. Yi) to (Xi+l, Yi+l) , assuming additions of subscripts are modulo 4
(that is, 4 + 1 = 1). A general point on this side of the square, (xi. Yi), is given by
Two-dimensional Coordinate Geometry 47
In fact J,L :l - J,L is the ratio in which the side is bisected. If J,L is fixed and the four
points { (xi, yi) Ii = 1, 2, 3 ,4 } are calculated in the above manner, then the sides
of the new square make an angle ex = tan " ! [J,L/(l - J,L)] with the corresponding
side of the outer square. So, by keeping J,L fixed for each new square , the angle
between consecutive squares remains a constant ex. In listing 3.1 , which generat-
ed figure 3.1, there are 21 squares and J,L =0.1.
Listing 3.1
100 RE~ square outside square etc.
110 LET start = 9700: LET setorigin = 9600 : LET moveto = 9500
: LET Lineto = 94f.l0
120 LET HORIZ = 3: LET VERT = 2.1
130 GO SUB start
14111 LET XMOVE = HORIZ*0.5: LET YMOVE = VERT*0.5
15111 GO SUB setori gi n
16111 DIM X(4): DIM Y(4): DIM V(4): DIM W(4)
170 DATA 1,1,1,-1,-1,-1,-1,1
179 REM initiaLise first square.
18111 FOR 1=1 TO 4: READ X(l),Y(l): NEXT I
189 REM set fo?J vaLue and draw 20 squares.
19111 LET MU = 111.1: LET UM = 1 - MU
21110 FOR I = 1 TO 21
21118 REM join four vertices of square (X(J),Y(J» J=1:4.
21119 REM caLcuLate next four ver t i ce s. CVCJ),W(J» J=1:4.
210 LET XPT = X(4): LET YPT = Y(4): GO SUB moveto
22111 FOR J = 1 TO 4
230 LET XPT = X(J): LET YPT = Y(J): GO SUB Lineto
24111 LET NJ = J + 1: IF NJ = 5 THEN LET NJ = 1
25111 LET V(J) = UM*XCJ) + MU*XCNJ)
260 LET W(J) = UM*Y(J) + MU*YCNJ)
270 NEXT J
279 REM copy arrays V and W into X and Y.
28111 FOR J =1 TO 4
29111 LET X(J) =
VCJ): LET YCJ) = W(J)
300 NEXT J
310 NEXT I
320 STOP
It is useful to note that the vector combination form of a line can be re-
organised
When given in this new representation the vector PI can be called the base vector,
and (P2 - PI) can be called the directional vector . In fact any point on the line
can stand as a base vector; it simply acts as a point to anchor a line that is parallel
to the directional vector. This concept of a vector acting as a direction needs
some further explanation. We have already seen that a vector pair, (x, y) say,
may represent a point; a line joining the coordinate origin to this point may be
thought of as specifying a direction - any line in space that is parallel to this
line is defined to have the same directional vector. We insist that the line goes
48 Advanced Graphics with the Sinclair ZX Spectrum
Figure 3.1
from the origin towards (x, Y), the so-called positive sense ; a line from (x, y)
towards the origin has negative sense.
This dual interpretation of a vector, as a point or a direction , is used in the
following example.
Example 3.2
Draw a dashed line , with 13 dashes (and hence 12 spaces between dashes) from
point PI == (x I, Y d to P2 == (x 2, Y 2) ' This problem is solved by finding the 26
equi-spaced points on the line ; that is PI + i/25 (P2 - PI) where i varies from
o(at PI) to 25 (at P2)' We draw consecutively or move between neighbouring
points using the CalComp 'plot' (listing 2.8). There is no need to store the values;
we already have PI , so, by adding 1/25(P2 - pd each time, we can move through
all the required points (see listing 3.2).
Listing 3.2
Exercise 3.1
Experiment by drawing different types of dashed lines: for example, (a) the size
of the dash could be twice that of the space between; (b) the size of the dash
could be a fixed numerical value and the number of dashes unknown ; (c) the
dashes could vary in size, alternating between large and short dashes, where the
relationship between the types of dashes and the spaces could be input variables.
This base and direction representation is also very useful for calculating the
point of intersection of two lines, a problem that frequently crops up in two-
dimensional graphics . Suppose we have two lines p + uq and r + As, where
p == (Xl. Yl) , q == (X2' 12), r == (X3. Y3) and s == (X4' Y4) for -00 <p., A < 00. We
need to find the unique values of p. and Asuch that
p+p.q=r+As
that is, a point that is common to both lines. This vector equation can be written
as two separate equations
Xl -r u « X2 =X3 +A X X4 (3.1)
Yl + P. X Y2 = Y3 + A X Y4 (3.2)
(3.3)
(3.4)
If (X2 x Y 4 - Y2 X X4) = 0 then the lines are parallel and there is no point of
intersection (p. does not exist), otherwise
(3.5)
and similarly
The solution becomes even simpler if one of the lines is parallel to a coordin-
ate axis. Suppose this line is X =d, then we can set r == (d, 0) and s == (0, I),
which when substituted in equation (3.5) gives
50 Advanced Graphics with the Sinclair ZX Spectrum
/l=(d-Yd/Yz
Naturally if both lines are parallel then the denominator in these equations
becomes zero and we get an infinite result , because the two parallel lines do not
intersect.
Example 3.3
Find the point of intersection of the two lines (a) joining (1, -1) to (-1 , -3) and
(b) joining (1, 2) to (2, - 2).
The lines may be written
/l = (1 - 1) x - 4 - (2 + 1) x 2 = -1/2
(-2 x-4-(- 2) x2)
Exercise 3.2
Experiment with this concept of vector representation of two-dimensional space.
You can make up your own questions: it is easy to check that your answers are
correct. Consider example 3.2 . We know that (2, 0) lies on the first line because
we used the value /l = -1/2 : our answer is correct if it also lies on the second line ;
it does with A= 1/2.
Exercise 3.3
Write a program that reads in data about two straight lines (it can be either in the
form of equations , or in the base/directional vector form) and then calculates
their point of intersection (if any).
Two-dimensional Coordinate Geometry 51
Clipping
These values are calculated, when needed, inside the algorithm program .
If the two points at the end of the line segment - that is, (XA , YA) and
(XB, VB) - have parameters IXA and IYA, and IXB and IYB respectively, then
there are a number of possibilities to consider.
(i) If IXA = IXB =1= 0 or IYA = IYB =1= 0, then the whole line segment is outside
the rectangle and hence may be safely ignored; for example, line AB in figure
3.2 .
(ii) If IXA = IYA = IXB = IYB = 0, then the whole line segment lies in the
graphics area and so the complete line must be drawn; for example, line CD.
(iii) The remaining case must be considered in detail. If IXA =1= 0 and/or IYA =1= 0
then the point (XA, YA) lies outside the rectangle and so new values for XA and
YA must be found - to avoid confusion we will call these XA' and YA'. (XA ',
YA') is the point on the line segment nearer to (XA, YA) where the line cuts the
graphics area . The formula for this calculation was considered above; that is, the
intersection of a line with another line parallel to a coordinate axis. If the line
misses the rectangle, then we define (XA', YA') to be that point where the line
52 Advanced Graphics with the Sinclair ZX Spectrum
E ~
•
>-
H
r~
•
.. ~
Figure 3.2
cuts one of the extended vertical edges. If IXA = IYA =0 then (XA' , YA') ==
(XA, VA). The point (XB', YB') is calculated in a similar manner; see the algor-
ithm given by routine 'clip' in listing 3.3 . The required clipped line is that joining
(XA' , YA') to (XB', YB'). If the original line misses the rectangle then the
algorithm ensures that (XA' , YA') = (XB' , YB') and the new line segment
degenerates to a point and is ignored. For example, EF is clipped to E'F' , GH
is clipped to GH' (G = G') and IJ degenerates to a point I' = J'.
Thus 'clip ' takes the two pixel end points of the line , (XA, VA) and (XB, YB),
and transforms them into the centred system. It then discovers which of the
above three possibilities is relevant and deals with it thus : (i) exit the routine
immediately ; (ii) join the two points; or (iii) calculate the 'dashed' points and
join them with a line.
Listing 3.3 also includes a new version of 'lineto' routine that calls 'clip '
instead of PLOT and DRAW, thus enabling it to cope with the problem of join-
ing lines anywhere in space. From now on always use this new version of 'lineto'.
It will prove invaluable, especially in the study of three -dimensional objects .
Exercise 3.4
Use this altered routine in the programs of chapter 2. Choose values of HORIZ
and VERT in such a way that some lines in the diagrams go outside the graphics
area.
Two-dimensional Coordinate Geometry 53
Listing 3.3
841<1£) REM cLip
8401 REM IN XA,YA,XB,YB
8409 REM change coordinate system.
84121 LET XA = XA - 127.5: LET YA = YA - 87.5: LET XB = XB - 127.5
: LET YB = YB - 87.5
8419 REM fine: the sector vaLues of two points (XA,YA) AND (XB,YB).
84221 LET IXA = 0: IF ABS XA > 127.5 THEN LET IXA = SGN XA
84321 LET IYA = 21: IF ABS YA > 87.5 THEN LET IY~ = SGN YA
8440 LET IXB = 21: IF ABS XB > 127.5 THEN LET IXB = SGN XB
84521 LET IYB = 0: IF ABS YB > 87.5 TH£:N LET lYB = SGN YB
8459 REM points in same off-screen sector then return.
8460 IF IXA*IXB = 1 OR IYA*IYB = 1 THEN RETURN
8470 IF IXA = 0 THEN GO TO 8521~
8479 REM move 1'st point to nearer x-edge .
8480 LET XX = 127.5*IXA: LET YA = YA + (YB - YA)*(XX - XA)/(XB - XA)
: LET XA = XX
8490 LET IYA = 0: IF ABS YA > 87.5 THEN LET IYA = SGN YA
852121 IF IYA = 0 THE~j GO TO 85221
8509 REM move 1'st point to nearer y-edge.
8510 LET YY = 87.5*IYA: LET XA = XA + (Xe - XA)*(YY - YA)/(YB - VA)
: LET YA = YY
85221 IF IXB = Il THEN GO TO 85521
8529 REM move 2'nd point to nearer x-edge.
85321 LET XX = 127.5*IXB: LET YB = YA + (YE - YA)*(XX - XA)/(XB - XA)
: LET XB = XX
8540 LET ;''''B = 21: IF ABS YB > 87.5 THEN LET IYB = SGN YB
855Ui IF IYB = 0 Tfl EN GO TO 85721
8559 REM move Z'nc point to nearer y-edge.
8560 LFT YY = 87.5*II'B: LET ;(E: : XA + (XB - XA)*(YY - YA)/(YB - VA)
: LET YB = n
857Vr IF ABS (XA - XB) < 21.0212121211 AND ABS (Y,. - VB) < 21.21212121211 THEN RETURN
8579 RE~1 pl ot r.or-r cc i r.ci dent poi nt s ,
85821 LET XA = INT (XA + 128): LET YA = INT (YA + 88)
: LET XB = INT (XB + 128): LET YB = INT (YB + 88)
8590 PLOT XA,YA: DRAII XB - XA,YB - Y.\: RETURN
94021 REM Lineto/ cLipping
9401 REV IN : XPT,YPT,XPEN,YPEN
9401 REM OUT: XPEN,YPEN
941e: LET XA=XPEN: LET YA=YPEN
94221 LET X r [ ~ = F N X(XPT)
94321 LET YPEN=FN Y(YPT)
9440 LET XB=XPEN: LET YB=YPEN
94521 GO SUB cLip
9460 HUL' FN
Returning to the use of a vector (q == (x, y) :1= (0, 0), say) representing a
direction, we note that any positive scalar multiple kq, for k > 0, represents the
same direction and sense as q. (If k is negative then the direction has its sense
inverted). In particular, setting k = 1/ I q I produces a vector (x/V(x 2 +y2),
y/V(x 2 +y2)) with unit absolute value.
Thus a general point on a line, p + J.LQ, is a distance Illql from the base point
p, and if Iql = 1 (a unit vector) then the point is a distance 1111 fromp.
We now consider the angles made by directional vectors with various fixed
directions. Suppose that 0: is the angle between the line joining 0 (the origin) to
q == (x , y) , and the positive x-axis. Then x = IqI x cos 0: andy = Iq I x sin 0:; see
figure 3.3 - there are similar figures for the three other quadrants.
54 Advanced Graphics with the Sinclair ZX Spectrum
IQ I C O SO
Figure 3.3
If q is a unit vector (that is, I q I = 1) then q == (cos 0:, sin 0:). We note that sin 0: =
cos (0: - rr/2) for all values of 0:. Thus we can rewrite q = (cos 0:, cos (0: - rr/2»,
but 0: - rr/2 is the angle that the vector makes with the positive y-axis. Hence the
coordinates of a unit directional vector are called its direction cosines, since they
are the cosines of the angle that the vector makes with the corresponding positive
axes.
Before continuing , we should take a look at the trigonometric functions avail-
able in BASIC: SIN and COS, and the inverse function ATN. SIN and COS are
functions with one parameter (an angle given in radians) and one result (a value
between -1 and +1). The ATN function takes any value and calculates the angle
in radians (in the so-called principal range between -rr/2 and +rr/2) whose
tangent is that value.
This leads us to the problem of finding the angle that a general direction
q == (x, y) makes with the positive x-axis , which is solved by routine 'angle' given
in listing 3.4; 'angle' will be of great use in later chapters when we consider three-
dimensional space.
Listing 3.4
Figure 3.4
Now suppose we have two directional vectors (a, b) and (c, d); for simplicity
we can assume that they are both unit vectors and they pass through the origin
(see figure 3.4). We wish to calculate the acute angle, 0::, between these lines.
From the figure we note that OA =y(a 2 + b 2 ) = I and OB =y(c 2 +d 2 ) = 1.
So by the Cosine Rule
f(x,y)==ay - bx-c
All, and only, those points with the property f(x, y) = 0 lie on the curve. This
representation divides all the points in two-dimensional space into three sets,
namely f(x, y) = 0 (the zero set), f(x, y) > 0 (the positive set) and f(x, y) < 0
(the negative set). If the function divides space into the curve and two other
connected areas only (that is, any two points in a connected area can be joined
by a curvilinear line that does not cross the curve), then these areas can be
identified with the positive and negative sets defined by f . However, be wary,
there are many elementary functions (for example,g(x, y) = cos (y) - sin (x))
that define not one but a series of curves and hence divide space into possibly an
infinite number of connected areas (note g(x, y) = g(x + 2m1T,y + 2n1T) for all
integers m and n). So it is possible that two unconnected areas can both belong
to the positive set.
Note that the functional representation need not be unique . We could have
put the line in an equivalent form
f'(x,y)==bx +c -ay
in which case the positive set of this function is the negative set of our original,
and vice versa.
The case where the curve does divide space into two connected areas is very
useful in computer graphics, as we shall see in a study of two-dimensional and
(especially) three-dimensional graphics algorithms. For example, take the straight
line
f(x,y)==ay - bx-c
where a point (x 1, y.) is on the same side of the line as (x 2, 12) if and only if
f(xl> y.) has the same non-zero sign asf(x2, Y2)' The functional representation
tells us more about a point (x 1, Y 1 ) than just which side of a line it lies - it also
enables us to calculate the distance of the point from the line.
Suppose we have the above line, then its direction vector is (a, b). A line per-
pendicular to this will have direction vector (-b, a) (why? the product of the
tangents of two mutually perpendicular lines is -1: see McCrae, 1953). So the
point q on the line closest to the point p == (x 1, Y 1) is of the form
Two-dimensional Coordinate Geometry 57
that is, a new line joining P to q is perpendicular to the original line. Since q lies
on this original line
and hence
where i = 1, ..., n, and the addition in the subscripts is modulo n (that is, n + j ==
< n). Try to explain why these formulae do actually describe the line
j for I ";;;'j
segments!
This systematic definition of the lines enables us to define the inside of the
convex area. Any given line segment, say the one joining Pi to Pi+l for some i, is
such that the points inside the body must lie on the same side of this line as the
remaining vertices of the polygon, in particular Pi+2' So the inside is given by
Example 3.4
Suppose we are given the convex polygon with vertices (1 ,0), (5 ,2), (4, 4) and
(-2, 1) (see figure 3.5) . In this order the vertices obviously have an anti-clock-
wise orientation. Are the points (3,2), (1 ,4), (3, 1) inside, outside or on the
boundary of the polygon? What is the distance of (4 ,4) from the first line?
I ,~
~j
I S . .,:; ; .
,::.
Figure 3.5
[1 (x, y) == (5 - 1) x (y - 0) - (2 - 0) x (x - 1) == 4y - 2x + 2
[2 (x, y) == (4 - 5) x (y - 2) - (4 - 2) x (x - 5) == -y - 2x + 12
[3(x,y)==(-2-4)x (y-4) -(1-4) x (x-4)==-6y+3x+12
[4 (x, y) == (1 + 2) x (y - 1) - (0 - 1) x (x + 2) == 3y + x-I
Exercise 3.5
Imagine two convex polygons that intersect one another. The area of inter-
section is also a convex polygon . Use the methods mentioned in this chapter to
calculate the vertices of the new polygon .
Having dealt with the functional representation of a line, what about the
parametric form? We noted that this form is one where the x-coordinate andy-
coordinate of a general point on the curve are given in terms of parameter(s)
(which might be the x-value and the y-value themselves), together with a range
for the parameter. So we have already seen a parametric form of a line: it is
simply the base and directional representation
J.L is the parameter, and x I + J.L x X2 and Y I + J.L x Y 2 are the respective x -value
andy-value, depending only on variable J.L .
We can also produce functional representations and parametric forms for
most well-behaved curves. For example, a sine curve is given by f(x , y) =Y -
sin (x) in functional representation, and by (x, sin (x)) with - 0 0 <x < 00 in its
parametric form . The general conic section (ellipse, parabola and hyperbola) is
represented by the general function .
f(x, y) =a x x 2 + b X y2 + h x x x y + fx x + g x y + c
Examp/e3.5
Suppose we wish to draw a circular ball (radius r) disappearing down an elliptical
hole (major axis a, minor axis b), see figure 3.6. Parts of both the ellipse and
circle are obscured .
Let the ellipse be centred on the origin with the major axis horizontal, and
the centre of the circle a distance d vertically above the origin. The ellipse has
functional representation
To generate the picture we must find the points (x, y) common to the circle and
ellipse (if any) . As a useful demonstration we shall mix the representations in
searching for a solution, using the functional representation for the circle and the
parametric form of the ellipse.
So we are searching for the points (x, y) == (a x cos a, b x sin 0:) on the
ellipse, which also satisfy I« (x,y) = O. That is
This is a simple quadratic equation in the unknown sin ex, which is easily solved
(the quadratic equation Ax 2 + Bx + C =0 has two roots (-B ± y(B 2 - 4 x A x
C»)/(2 x A)). For each value of sin 0: we can find values for 0: with 0 ~ 0: ~ 21T
(if they exist) and we can then calculate the points of intersection (a x cos Cl:,
b x sin 0:).
There is no hard and fast rule regarding which representation to use in any
given situation - afeel for the method is required and that comes only with
experience .
Two-dimensional Coordinate Geometry 61
Figure 3.6
Exercise3.6
Write a program that will draw figure 3.6.
Complete Programs
In chapter 2 we saw the need to translat e pictures of objects about the screen.
Rather than perpetually change the screen coordinate system , it is conceptually
much easier to define an object in the most simple terms possible (as vertices in
the form of pixel or coordinate values, together with line and area information
related to the vertices), and then transform the object to various parts of the
screen while keeping the screen coordinate system fixed. We shall restrict our-
selves to linear transformations (see below). It will often be necessary to trans-
form a large number of vertices, and to do this efficiently we use matrices.
Before looking at such matrix representations we should explain exactly what is
meant by a matrix , and also by a column vector. In fact we restrict ourselves to
square matrices ; to 3 X3 (said 3 by 3) for the study of two-dimensional space,
and later we shall use 4 X 4 matrices when considering three-dimensional space.
Such a 3 X 3 matrix (A say) is simply a group of real numbers placed in a block
of3 rows byJ columns: a column vector (D say) is a group of numbers placed
in a column of 3 rows
A general entry in the matrix is usually written A i j , the first subscript denotes
the i th row, and the second subscript the/h column (for example ,A 2 3 repre-
sents the value in the second row, third column). The entry in the column vector,
D i , denotes the value in the i th row. All these named entries will be explicitly
replaced by numerical values and it is important to realise that the information
stored in a matrix or column vector is not just the individual values but also the
position of these values within the matrix or vector. Naturally BASIC programs
are written along a line (no subscripts or superscripts), and hence matrices and
vectors are implemented as arrays and the subscript values appear inside round
brackets following the array identifier.
Matrices can be added. Matrix C = A + B , the sum of two matrices A and B , is
defined by the general entry Ci j thus
Matrix Representation of Transformations on Two-dimensional Space 63
B jj = k X A jj 1 ~ i, j ~ 3
The i th row element of the new column vector is the sum of the products of the
corresponding elements of the i th row of the matrix with those in the column
vector.
Furthermore, we can calculate the product (matrix) C = A X B of two mat-
rices A andB
We take the sum (in order) of the elements in the i th row of the first matrix
multiplied by the elements in the jth column of the second . It should be noted
that the product of matrices is not necessarily commutative ; that is, A X B need
not be the same as B XA . For example
010
01\) (0010
01) (0100
10) (0001) (00010) (1001
00)
(o100 100 001 100 100 010
X = but lOX 1 =
Experiment with these ideas until you have enough confidence to use them in
the theory that follows . For those who want more details about the theory of
matrices we recommend books by Finkbeiner (1978) and by Stroud (1982).
There is a special matrix called the identity matrix I (sometimes called the
unit matrix)
1a a a0)
1=
(a a 1
1
Also for every matrix A we can calculate its determinant det (A)
Any matrix whose determinant is non-zero is called non-singular, and with zero
determinant singular. All non-singular matrices A have an inverse A -1 , which has
the property that A X A-I = I and A-I X A = I . For methods of calculating an
inverse of a matrix see Finkbeiner (1978) ; we give a listing in chapter 7 (listing
7.5), which uses the Adjoint method.
Wenow consider transforming points in space. Suppose a point (x, y) -
'before' - is transformed to (x', y') - 'after'. We understand the transformation
completely if we can give equations relating the 'before' and 'after' points. A
linear transformation is one that defmes the 'after' point in terms of linear com-
binations of the coordinates of the 'before' point ; that is, the equations contain
only multiples of x, y and additional real values - it includes neither non-unit
powers or multiples of x and y, nor other variables. Such equations may be
written
x' = A 11 X X +A 12 X Y + A 13
y'=A 21 X x+A n Xy+A 2 3
The A values are called the coefficients of the equation. As we can see, the result
of the transformation is a combination of multiples of x-values, y-values and
unity . We may add another equation
For this to be true for all values of x and y, we see that A 31 = A 32 = 0 and
A 33 = 1. This may seem a pointless exercise but we shall see that it is very useful.
For if we set each point vector (x, y) (also called a row vector for obvious
reasons) in the form of a three-dimensional column vector
then the above three equations can be written in the form of a matrix multiply-
ing a column vector
and postmultiply the row vector by the matrix so that the above equations in
matrix form become
A ll
(x',y' ,1)=(x,Y,1)X A 12
(
A 13
Note that this matrix is the transpose of the matrix of coefficients in the equa-
tions. This causes a great deal of confusion among those who are not confident
in the use of matrices. It is for this reason that in this book we keep to the
column vector notation. As you get more practice in the use of matrices it is a
good idea to rewrite some (or all) of the following transformation routines in
the other notation. It is not important which method you use finally, as long as
you are consistent. (Note the transpose B of a matrix A is given by B i; = A ji »
where 1 ~ i, j ~ 3.)
Combination of Transformations
Listing 4.1
9101' REM muL t2
9101 REM IN : A(3,3),R(3,3)
9102 REM OUT : R(3,3)
911 I' FOR I = 1 TO 3
9120 FO R J= 1 TO 3
9130 LET AR = 0
9140 FOR K = 1 TO 3
9150 LET AR = AR + A(I,K )*R(K, J)
9160 NEXT K
9170 LET B(I,J) = AR
9180 NEXT J
9190 NEXT I
9201' FOR I = 1 TO 3
9210 FOR J = 1 TO 3
9220 LET R(I,J) = B(I,J)
9230 NEXT J
9240 NEXT I
9250 RETURN
Translation
A 'before ' point (x, y ) is moved by a vector (TX, TY) to (x', y') say. This
produces the equations
x' = 1 x x + 0 x y + TX
y' = 0 x x + 1 x Y + TY
o1 01 TX)
TY
( 001
And a routine, 'tran2', for generating such a matrix A , given the values TX and
TY is given in listing 4 .2.
Listing 4.2
9000 REM tran2
9001 REM IN : TX,TY
9002 REM OUT : A(3,3)
9010 FOR I = 1 TO 3
9020 FOR J = 1 TO 3
9030 LET A(I,J) = 0
9040 NEXT J
9050 LET A(I,I) = 1
9060 NEXT I
9070 LET A(1,3) = TX: LET A(2,3) = TY
9080 RETURN
Scaling
The x-coordinate of a point in space is scaled by a factor SX, and the j-coordtn-
ate by SY, thus
x' = SX x x + 0 x y + 0
y' = 0 x x + SY x y + 0
SX
oSY 0
0 0)
(
001
Usually SX and SY are both positive, but if one or both are negative this creates
a reflection as well as a scaling. In particular, -ifSX = -1 and SY = 1 then the
point is reflected about the y-axis. A program segment, 'seale2', to produce such
a scaling matrix A given SX and SY is given in listing 4.3.
Listing 4.3
COS 0 -sin 0
sin 0 cos 0
(
o 0
Listing 4.4
8600 REM rot2
8601 REM IN : THETA
8602 REM OUT : A(3,3)
8610 FOR I = 1 TO 3
8620 FOR J = 1 TO 3
8630 LET A(I,J) = 0
8640 NEXT J
8650 NEXT I
8660 LET A(3,3) = 1
8670 LET CT = COS THETA: LET ST = SIN THETA
8680 LET A(1,1) = CT: LET A(2,2) = CT
8690 LET A(1,2) = -ST: LET A(2,1) = ST
8700 RETURN
Inverse Transformations
For every transformation there is an inverse transformation that will restore the
points in space to their original position. If a transformation is represented by a
matrix A, then the inverse transformation is represented by the inverse matrix
A-I . There is no need to calculate this inverse using listing 7.5, we can find it
directly by using listings 4.2, 4.3 and 4.4, with parameters derived from the
parameters of the original transformation
We are often required to draw a given object at various points on the screen, and
at arbitrary orientations. It would be very inefficient to calculate by hand the
coordinates of vertices for each position of the object and input them to the
program. Instead we define first an arbitrary but fixed coordinate system for
two-dimensional space, which we call the ABSOLUTE system. Then we give the
coord inates of the vertices of the object in some simple way , usually about the
origin, which we call the SETUP position . Lines and areas within the object are
defined in terms of the vertices . We can then use matrices to move the vertices of
the object from the SETUP to the ACTUAL position in the ABSOLUTE system.
The lines and areas maintain their relationship with the now transformed vertices.
The matrix that relates the SETUP to ACTUAL position will be called P through-
out this book (we sometimes give it a letter subscript to identify it uniquely from
other such matrices). Because of the restriction of not passing arrays as para-
meters into subprograms, we shall not normally explicitly generate array P,
instead it will be implicitly used to update the array R .
no tilt of the head (we call this the OBSERVED position) . Therefore we generate
another matrix that will transform space so that the eye is moved from its
ACTUAL position to this OBSERVED position . The ACTUAL to OBSERVED
matrix is named Q throughout this book , and is achieved by first translating all
points in space by a vector (-DX, -DY), matrix A, and then rotating them by
an angle -Q, matrix B (note the minus signs!). Thus Q::: B X A, which is generat-
ed in routine '100k2', listing 4.5. Normally we do not calculate Q explicitly, as
usually it is used only to update R ; however, if it is necessary to use the values of
the matrix repeatedly then obviously it is sensible to store Q.
70 Advanced Graphics with the Sinclair ZX Spectrum
Listing 4.5
820~ REM Look2/ger.eraL
8202 REM OUT : R(3,3)
821~ INPUT "(DX,DY) ";DX;",";DY
8220 INPUT "ALPHA ";ALPHA
8229 REM Look at (DX,DY).
823~ LET TX = -DX: LET TV = -DY
8240 GO SUB tran2: GO SUB muLt2
8249 REM tiLt head through ALPHA rad ians.
8250 LET THETA = -ALPHA
8260 GO SUB rot2: GO SUB muL t2
8270 RETURN
Drawing an Object
We can draw pictures that contain a number of similar objects. There is no need
to produce a new routine for each occurrence of the object, all we do each time
is calculate a new SETUP to OBSERVED matrix and enter this into the same
routine. Naturally we shall require one routine for each new type of object in
the picture. The final picture is achieved by the execution of a routine we name
Matrix Representation of Transformations on Two-dimensional Space 71
'scene2' , which will be called from the standard main program (listing 4.6). This
main program simply defines the labels of the various subprograms , declares
arrays , centres the graphics area having INPUT HORIZ and VERT, and finally
calls 'scene2'.
Listing 4.6
10~ REM main pr 0g r ~m
1~9 REM def ine i de nt i f i er s for init iaLi sat i un and 2-D pLot routines.
110 LET start = 970~: LET se t ori gi n = 9600: LET moveto = 950~
: LET Lineto = 940~ : LET cLip = 840~
120 LET rot Z ~ E60~: LET angLe = 880~: LET scaLe2 = 890~: LFT t ran 2 = 90~0
: LET muLt2 = 9100: LET ; dR2 = 930~
130 LET scene2 = 600~: LET l cok2 = 820~
139 REM initiaLise and cent re graph ic s area.
140 INPUT "HORIZ ",HORIZ,"VERT ",VERT
150 GO SUB sta rt
160 LET XMOVE = HORIZ*0.5: LET YtlCVE = VERT*0 .5
170 GO SUB setorigin
139 REM set the scene.
180 GO SUB scene2
190 STOP
'scene2 ' will first call '100k2' and generate Q, and if more than one object is
to be drawn then we store it. For each individual object (or brick) we calculate a
matrix P and call the required construction routine using R = Q X P. All the
bricks finally build into the finished picture. To distinguish between different
occurrences of these matrices in what follows, we sometimes add a subscript to
the names P and R .
This modular approach to solve the problem of defining and drawing a
picture may not be the most efficient, but from our experience it does greatly
clarify the situation for beginners, enabling them to ask the right questions about
constructing a required scene. Also when dealing with animation we shall see
that this approach will minimise problems in scenes where not only are the
objects moving relative to one another, but also the observer himself is moving. .
Naturally if the head is upright then matrix Q can be replaced by a call to
'setorigin', which changes the screen coordinate system. Or if the eye is looking
at the origin, head upright , then Q is the identity matrix I; hence it plays no part
in transforming the picture and the '100k2' routine may be ignored. We shall
make no such assumptions and work with the most general situation : it is a use-
ful exercise throughout this book for the reader to cannibalise our programs in
order to make them efficient for specific cases. It is our aim to explain these
concepts in the most general and straightforward terms, even at the expense of
efficiency and speed. The reader can return to these programs when he is ready
and fully understands these ideas of transforming space. Later we shall give some
hints on how to make these changes, but at the moment this would only confuse
the issue.
However, the most important reason for this modular approach will be seen
when we come to draw pictures of three-dimensional objects . We shall define
72 Advanced Graphics with the Sinclair ZX Spectrum
Example 4.1
Consider a simple space ship SETUP pointing in the positive x -direction (that is,
making an angle 0 radians with the x-direction). The ship is defined by five line
segments joining, in order, the points (3, 0), (0 , 0) , (-1, 1), (2, 0), (-1 , -1) and
back to (0,0). See figure 4.1 ; which is a ship drawn on a screen 5 units by 3
units , where the SETUP to ACTUAL matrix is the identity and the ACTUAL to
OBSERVED matrix is such that the observer is looking at the point (1, 0) with
head upright. Listing 4 .7 gives the necessary routine 'scene2' that moves the
object into position, and listing 4.8 is the required construction routine 'ship'.
Note that 'ship', which uses matrix R to transform the vertices (and hence the
object) into their OBSERVED position, does not store the vertex values for this
position in a permanent data-base. Instead the values are kept in arrays X and Y
for the duration of the routine, and if the routine is re-entered to draw another
space ship then these array locations are used again.
~
~ -~'0)
(0
----------
10)/
/ / ------
.--
---------
/ /
/ "
~
(-1 1 - :1. )
Figure 4.1
Figure 4.2
Matrix Representation of Transformations on Two-dimensional Space 73
Listing 4. 7
60~~ REM sce ne2/l ook2 ; s~i~ ( n ct stored)
6010 DIM X(6): DIM V(6)
6020 DIM A(3,3): DIM B(3,3): DIM R(3,3)
6030 LET shi p =650~
6039 REM place the observer.
6040 GC SUB idR2: GO SUB look2
6049 REM def ine and draw oe j e ct .
6050 GO SUB shi p
6~60 RETU RN
Listing 4.8
650~ REM ship/ not stored
6509 REM IN : R(3,3)
6510 DATA 3,0,0,0,-1,1 ,2,0,-1,-1,0,0
6520 RESTORE sh ip
6530 FOR I = 1 TO 6
6539 REM read coordinates of SETUP object.
6540 READ XX, VV
6549 REM move object i nto OBSE RVED pos it ion.
6550 LET XCI) = XX*R (1,1) + VY*R( 1, 2) + R(1,3)
6560 LET V( I ) = XX*R(2,1) + VY*R( 2,2) + R(2,3)
6570 NEXT I
6579 REM join vertices in order.
6580 LET XPT = X(1) : LET VPT = Y(1): GO SUB moveto
6590 FOR I = 2 TO 6
660~ LET XPT = XCI ): LET VPT = Y(I): GO SUB l ineto
6610 NEXT I
6620 RETU RN
Example 4.2
Suppose we wish to draw figure 4.2, which includes four space ships labelled (a),
(b) , (c) and (d) on a screen 60 units by 40 units. For simplicity in this picture we
assume Q is the identity matrix, so the head is upright and the eye looks at the
SETUP origin. Ship (a) is placed identically to its SETUP position; that is,
R a =I , whereas ship (b) is moved from its SETUP to ACTUAL position by the
following transformations
A =
(
4 0 0) (V3/2 - 1/2 0)
0 2 0 B = 1/2
(1 o
v'3/2 0 C = 0 1 4
6)
001 0 0 1 0 o 1
Y
(
2 3 -1 6)
~ ~3 1 X
(3)
~ = ~0
(6V3 + 6)
etc.
When returned to normal vector form we see that the five vertices have been
transformed to (6V3 + 6 ,10), (6, 4) , (5 - 2y3 , y3 + 2) , (4.j3 + 6, 8) and
(7 - 2y3, 2 - y3) respectively .
Ship (c) is ship (b) reflected in the line 3y = - 4x - 9. This line cuts the y-
axis at (0, - 3) and makes an angle 0: = cos"! (-3/5) = sin- 1 (4/5) = tan " !
(-3/4) with the positive x-axis. If we move space by a vector (0 , 3) , matrix D
say, th is line will go through the origin. Furthermore, if we rotate space by -0: ,
matrix E say , the line now is identical with the x -axis, Matrix F can reflect the
ship in the x-axis, £-1 puts the line back at an angle 0: with the x -axis, and
finally D - 1 returns the line to its original position. Matrix G = D- 1 X £-1 X
F X E X D will therefore reflect all the ACTUAL vertices of ship (a) about the
line 3y = - 4 x - 9, and R c =I XPc = G X Pb can therefore be used to draw ship
(c). That is, we use matrix Pb to move the ship to position (b) and th en G to
place it in position (c).
100) - 3/ 5 4/5
D=
(
0 1 3
001
£ =
(
- 4/5 - 3/ 5
o 0
and
1 ( -48 - 14.j3 7 - 24.j3 - 210)
Rc =- 14 -48y3 24+7y3 -170
25 0 0 25,
Figure 4 .2 is drawn using the new 'scene2' routine of listing 4.9 : note that
this 'scene2' does not call '100k2', since it is assumed that th e eye is looking at
the origin with the head erect. The main program and the 'ship' routine , as well
as all the other graphics package routi nes, sta y unchanged.
Matrix Representation of Transformations on Two-dimensional Space 75
Listing 4.9
60~0 REM scene2/ no Look2 ; 4 shi~s (net s t0r~L)
6010 elM X(6) : DI~ Y(6)
6020 DIM A(3,3): DI~ B(3,3) : DIM R(3,3)
6030 LET ship = 6500
6~39 REM OBSERVED=ACTUAL, no need to caLL 'Look2'.
6034 REM ship a).
6040 GO SUB i dR2 : GO SUB sh i p
6~49 REM sh ip b).
6050 LET SX = 4 : LET ~ Y = 2
6e60 GO SUB scaLe2: GO SUB muLt2
607e LET THETA " F!l6
6080 GO SUB rot2 : GO SUB muL t2
60ge LET TX " 6: LET TY = 4
6100 GO SUB tran2: GO SUB m~Lt2
611~ GO SUB sr ip
6119 REM ship c).
6120 LET AX " -3: LET AY = 4
6130 GO SUB angLe
6140 LET TX " 0: LFT TY " 3
6150 GO SUB tran2: GO SUB muLt2
616e LET T~ETA " -THETA
6170 GO SUB rot2: GO SUB muL t2
61Se LET SX = 1: LET SY = -1
6190 GO SUB sea Le2: G0 SUB n LiL t2
6200 LET THETA = -THETA
621 ~ GO SUB rct2: GO Sl'8 n.ul t2
6220 LET TX = 0: LET TY = -3
623~ GO SUB t rar2: GO SUB muLt2
6 2L.0 GO SUB shi p
6249 RE~ s hi p c).
6250 GO SUB i dR2
6260 LET TX = 6: LET n = 4
6 2 7~ GO SUB tran2: GO SUB muLt2
6280 LET TH ET F. = P!/6
(,290 GO SUB r et2 : GO SUB muL t2
6300 LET SX = 4: LET SY = 2
6310 GO SUB s ca Le2 : GO SUB muL :2
63?0 GC SU B s h i p
6330 RETUR'"
Exercise 4.1
In order to convince yourself that this program may be used to deal with the
general situation, you should run this program using non-zero values of DX, DY
or 0: so that the ACTUAL to OBSERVED matrix Q is not the identity matrix .
Your 'scene2 ' routine should call 'look2 ' to calculate Q, which must be stored .
Then for each object in the scene, in turn, calculate the SETUP to ACTUAL
matrix P (which 'mult2' places in R), premultiply it by Q (which has to be
copied into matrix A for use with 'mult2') and finally enter the construction
routine with the product matrix R = Q X P. Make sure that the 'lineto' routine
contains the 'clip' option or you will find your program fails when it tries to
draw outside the rectangle of the graphics area.
76 Advanced Graphics with the SinclairZX Spectrum
Exercise 4.2
Use the above routines to draw diagrams that are similar to figure 4.2, but where
the number, position and directions of the ships are read in from the keyboard.
You can produce routines to draw more complicated objects; we chose a very
simple example so that the algorithms would not be obscured by the complexity
of objects. The above method can deal with as many vertices and lines as the
Spectrum can handle within time and storage limitations. The objects need not
be line drawings only, you can draw coloured areas (polygons bounded by
transformed vertices).
Exercise 4.3
Using loops in the program we can draw ordered sequences of the objects; for
example, they can all have the same orientation but with points of reference
(the origin in the SETUP position) equally spaced along any line p + uq, We can
set up a loop with index parameter IJ. and draw one ship for each pass through
the loop . For each value of IJ. we can alter the parameters of translation in a
regular way within the loop (using IJ., p and q). The new values of these para-
meters are used to calculate a different SETUP to ACTUAL matrix for each
occurrence, and this moves the object into a new ACTUAL position. R = Q X
P = I X P is used to observe and draw each object on the screen . With these ideas,
construct a set of battle formations of the above type of ship on the screen .
Listing 4.1a
Listing 4.2a
90rlrl REM tran2
90rl1 REM IN : TX,TY
90rl2 REM OUT : A{3,3)
9010 DIM AC3,3)
9020 LET AC1,1l = 1 : LET AC2,2) = 1
9030 LET AC1,3) = TX: LET AC2,3) =
TY
9040 RETURN
Listing 4.3a
8900 REM sea Le2
8901 REM IN : SX, SY
8902 REM OUT : A{3,3)
8910 DIM AC3,3)
8920 LET AC1,1) = SX: LET AC2,2) SY
8930 RETURN
Listing 4.4a
860rl REM r ot 2
8601 REM IN : THETA
8602 REM OUT : A{3,3)
8610 DIM A{3,3)
8620 LET CT = COS THETA: LET ST =
SIN THETA
8630 LET A{1,1) = CT: LET AC2,2) =
CT
8640 LET AC1,2) = -ST: LET AC2,1) = ST
8650 RETURN
The construction of figure 4 .2 may seem rather contrived since the position
of the objects was chosen in an arbitrary way. However, in most diagrams the
positioning of objects will be well defined, the values being implicit in the
diagram required. See the example below.
Example 4.3
Write a program that draws an ellipse with major axis A, minor axis Band
centred at the point (CX, CY). The major axis makes an angle 8 with the positive
x-direction, Note that the order of transformations is important: first rotate and
then translate. If we wish to draw ellipses with major axishorizontal then we need
not use matrices, we can stay with the routine set in exercise 2.5 using ideas
78 Advanced Graphics with the Sinclair ZX Spectrum
similar to those in listing 2.9. Listing 4.10 gives a 'scene2' routine that reads in
data about the ellipse, calculates the SETUP to OBSERVED matrix and then
calls the construction routine 'ellipse' that draws the ellipse.
Listing 4.10
6000 REM scene2/ell ipse
6010 DIM A(3,3): DIM B(3,3): DIM R(3,3)
6020 LET e ll ipse = 6500
6030 INPUT "(Cx,cn "; CX; ","; CY,, "A ";A;",B ";B;", THETA "; THETA
6040 LET THFTA = -THETA
6049 REM ell ipse centred at (CX,CY) and til ted through angle THETA.
6050 GO SUB idR2
6060 GO SUB rot2: GO SUB mult2
6070 LET TX = CX: LET TY = CY
6080 GO SUB tran2: GO SUB mult2
6090 GO SUB ell ipse
6100 RETURN
6500 REM ell i pse
6501 REM IN : A,B,R(3,3)
6509 REM ellipse, major axis A, minor axis B.
6510 LET XPT = A*R(1,1) + R(1,3)
6520 LET YPT = A*R(2,1) + R(2,3)
6530 GO SUB moveto
65~ LET ALPHA = 0: LET ADIF = PI/100
6549 REM calculate point s (XPT,YPT) on ellipse, in OBSERVED pos ition.
6550 FOR I = 1 TO 200
6560 LET ALPHA = ALPHA + ADIF
6570 LET AA = A*COS ALPHA : LET BB = B*SIN ALPHA
6580 LET XPT = AA*R(1,1) + BB*R(1,2) + R(1,3)
6590 LET YPT = AA*R(Z,1) + BB*R(2,2) + R(2,3)
6600 GO SUB l ineto
6610 NEXT I
6620 RETURN
Exercise 4.4
Write a routine for drawing an individual matrix-transformable object (in this
case an astroid, shown in figure 4.3a) and then use the matrix techniques to
draw combinations of these objects (as in figure 4.3b). An astroid is a closed
curve with parametric form (R cos3 () fsin 3 ()) where 0";; () ..;; 21T, R being the
radius (the maximum distance from the centre of the object). The parameters
needed by this routine are the radius of the astroid and the transforming matrix.
Figure 4.3b is the combination of a large number of two different forms of the
astroid. One has radius 1 and is not rotated, the other has radius .J2 and is
rotated through rr/4 radians.
Exercise 4.5
Experiment with these matrix techniques. Write a subroutine that generates the
matrix necessary to rotate points in space by an angle () about an arbitrary point
(X, Y) in space (not necessarily the origin) . Also produce another routine that
generates the matrix that will reflect points about the general line ay =bx + c.
(Use the ideas given in example 4.2 for the production of ship (c).)
Matrix Representation of Transformations on Two-dimensional Space 79
.-----/
..
/
/
,J
~ ;/
\/1
(a) (b)
Figure 4.3
We mentioned earlier that certain situations arise where we need to store all the
information about a scene in a large data-base rather than lose the information
on leaving the construction routine. Our data-base will consist of arrays X and Y,
of length greater than or equal to NOV, the final number of vertices to be stored .
(These vertices can be stored in the SETUP, ACTUAL or OBSERVED position:
it depends on the context of the problem.) We also need to store information on
lines , in a two-dimensional array L whose first index is 1 or 2, and whose second
index is a number between 1 and a value greater than or equal to NOL, the final
number of lines in the scene. The Ith line joins the two vertices with indices
L(l,l) to L(2,I) : hence this information is independent of position, it simply
says which two vertices are joined by the Ith line. NOV and NOL are initialised
in the 'scene2 ' routine and incremented in the construction routines.
We now no longer require construction routines to draw lines, we use them
only to create the data-base of lines, vertices, etc. (transformed by the matrix R) .
After 'scene2' has constructed the final scene in memory it calls another routine
'drawit', which draws the final picture. The 'scene2' routine will be very similar
to those mentioned earlier ; for example, the routine for drawing figure 4.2 in
this new way will be that given in listing 4.9 with the three minor changes
listed below
6010 DIM X(20): DIM Y(20): DIM L(2,20)
6030 LET NOV=O : LET NOL=O: LET ship= 6500: LET drawit=7000
This is used in conjunction with listing 4.11, which gives the 'ship' construction
routine (which only sets up the data), and 'drawit ' routine.
Listing 4.11
Suppose we wish to produce different views of the same scene (again we use
figure 4.2 as an example); that is, the same SETUP to ACTUAL matrices P, but
different ACTUAL to OBSERVED matrices Q. The obvious solution is to create
a data-base for the scene with the vertices in the ACTUAL position. Now for each
new OBSERVED position we calculate Q and enter it into another 'drawit'
routine (see listing 4.12 - different from listing 4.11), which transfers each
vertex from its ACTUAL to OBSERVED position using Q, stores them in arrays
V and W, and recalls them when they are required for drawing. When using this
method to construct different views of figure 4 .2, only the 'scene2' and 'drawit'
routines differ from their earlier manifestations, and then only slightly. We give
them in listing 4.12.
Matrix Representation of Transformations on Two-dimensional Space 81
Listing 4.12
Exercise 4.6
Construct a dynamic scene. With each new view the ships will move relative to
one another in some well-defined manner. The observer also should move in
some simple way; for example, the eye starts looking at the origin, five views
later it is looking at the point (10 ,10), and with each view the head is tilted a
further 0.1 radian. You no longer need to INPUT the values of (DX, DY) and
ALPHA into 'look3' , instead they should be calculated by the program . After
you have read chapter 13 you can place these five pictures in store and recall
them as a 'movie' (if you have the 48K machine).
Exercise 4.7
Construct a scene that is a diagrammatic view of a room in your house - with
schematic two-dimensional drawings of tables , chairs, etc. placed in the room .
Each different type of object has its own construction routine, and the 'scene2'
routine should read in data to place these objects around the room. Once the
scene is set produce a variety of views, looking from various points and
orientations.
Or you can set up a line-drawing picture of a map, and again view it from
various orientations. The number of possible choices of scene is enormous!
Because we are using the 'clip' option in 'lineto' we can choose small values
for HORIZ and VERT, which has the effect of the observer zooming up close to
parts of a scene, and all external lines will be conveniently clipped off.
Complete Programs
We group the listings 3.4 ('angle'), 4.1a ('mult2' and 'idR2'), 4.2a ('tran2'),
4.3a ('scale'), 4.4a ('rot2 ') , 4.5 ('look2') and 4.6 ('main program') under the
heading 'lib2'.
I. .'lib l " 'lib2' , listings 4.7 ('scene2') and 4.8 ('ship'). Data required : HORIZ,
VERT, DX, DY and ALPHA. Try 8,5, 1, 1,0.5. Keep any four of these
values fixed and systematically make small changes in the other data value.
II. 'lib 1" 'lib2', listings 4.9 ('scene2') and 4 .8 ('ship'). Data required: HORIZ,
VERT. Try 30, 20; 200, 200 ; 200 ,150.
°
III. 'lib 1" 'lib2' and listings 4.1 ('scene2' and 'ellipse'). Data required: HORIZ,
VERT , CX, CY, A, B, THETA. Try 30, 20, 0, 0,12,9,0. Again fix all but
one of the values and change the remaining value systematically.
IV. 'lib 1" 'lib2' , listings 4.9 ('scene2' adjusted as per text) and 4.11 ('ship' and
'drawit'). Data required : as for II above.
V. 'lib1" 'lib2', listings 4.11 ('ship' but not 'drawit') and 4.12 ('scene2' and
'drawit ') . Data required: HORIZ, VERT, DX, DY, ALPHA. Try 60, 40, 0, 0,
0. Systematically change each of the data values in turn .
5 Character Graphics on the ZX
Spectrum
In the first chapter (listing 1.2) we saw how a small block or character may be
generated by eight binary numbers. Such a block of 8 by 8 pixels is known as a
character block . The Spectrum has three types of characters built in: the 96
character standard set, the 16 character block graphics set and the 21 character
user-defined graphics set. The latter two sets are accessed from the keyboard in
graphics mode . These characters are placed on the display when PRINT is used,
so we must take a closer look at this operation .
The PRINT command allows us to place characters on any of the 22 lines of
the upper screen, from top (0) to bottom (21), and at any of 32 positions along
each line, from left (0) to right (31). These are the character blocks and each
contains eight lines of eight pixels . Each line of eight pixels corresponds to a
value stored in one memory location ; each pixel corresponds to a binary digit in
an eight-digit binary number. This can be represented by a decimal number
between 0 and 255 . In the store there is a table of characters. For any given
character the PRINT command either finds the eight VALUEs from the table or
calculates them, and copies them into the appropriate display-me memory loca-
tions . This has the effect of drawing the character on the screen.
The table of data for the standard character set is stored in ROM, the permanent
Read Only Memory of the computer. There are eight pieces of data for each of
the 96 characters, thus the table consists of 768 (96*8) consecutive locations
starting at 15616 . Each character has a unique CODE number, see page 183 of
the Spectrum BASIC Handbook (Vickers, 1982). The table contains the data for
each of the characters in turn, starting with space (CODE 32) and ending with the
copyright symbol (CODE 127) . When the PRINT command requires the eight
pieces of data for a given character , it looks at the system variable CHARS (see
page 173 of the Spectrum BASIC Handbook (Vickers, 1982)) , which contains
the address of a location 256 (eight times the CODE for space) less than the start
of the character table . In order to find the address of the first of the eight pieces
of data it multiplies the CODE number of the character by eight and adds it to
84 Advanced Graphics with the Sinclair ZX Spectrum
CHARS. Finally PRINT copies the data to the display file and the character
appears on the screen.
Run the following program, which is based on listing 1.2. It demonstrates how
this process works by showing what calculations are performed. A detailed ex-
planation of how to work out the display-file locations for any character block is
given in chapter 13, but for the moment we shall continue to use block (0 ,0)
only . Figure 5.1 is an example run of this program using 'T as INPUT data.
Listing 5.1
10 CLS: INPUT "Which character";AS: IF AS ="" THEN GO TO 10
20 LET AS = AS(1): PRINT AT 2,0;""""iASi'''''' SELECTED. CODE ("iASi") = "iCODE AS
30 PRINT AT 4,0;"CALCULATION OF DATA LOCATION"
~ LET CHARS = PEEK 23606 + 256*PEEK 23607
50 PRINT AT 6,2i"CHARS POINTS TO "iCHARS
60 LET TABLE = CODE AS*8
70 PRINT AT 7,3;"CODE AS * 8 = ";TABLE
80 LET START = CHARS + TABLE
90 PRINT AT 8,17i"-------": PRINT AT 9,2i" DATA STARTS AT "iSTART
100 LET CORNER = 16384: PRINT AT 11 ,0;"TABLE LOC. VALUE SCREEN LOC."
110 FOR I = 0 TO 7
120 LET VALUE = PEEK (START + I): LET MEMORY = CORNER + 256*1
130 PRINT AT I + 13,2iSTART + I: PRINT AT I + 13,22;MEMORY
1~ PRINT AT I + 13,13iVALUE
150 POKE MEMORY, VALUE
160 NEXT I
170 INPUT "ANOTHER GO ? "iYS: IF YS = "n" THEN STOP
180 GO TO 10
?
•• ? .. SELECTED. CODE €?) = 63
CALCULATION OF DATA LOCAT ION
CHARS POINTS TO 15360
CODE AS .. a = 504
DATA STARTS AT 15864
TABLE LOC. VALUE SCREEN LOC.
15864- o 16384-
15865 60 1664-0
15866 66 16896
15867 4 17152
15868 8 174-08
15869 o 17664-
15870 8 17920
15871 o 18176
Figure 5.1
Exercise 5.1
Rewrite listing 5.1 so that it will allow you to decide whether or not to accept
each data VALUE before it is POKEd into the display flle. If one of the eight
Character Graphics on the ZX Spectrum 85
The address stored in CHARS is where you might expect to find data for the
character with CODE 0; however , characters with CODEs between 0 and 31 are
either control characters or unused . Since control characters are not displayed,
then no data are stored for them. Although CHARS points to this address, the
256 (32*8) bytes following it are not used and so are available for other purposes.
Furthermore, if a graphics mode character is selected for listing 5.1, then either
blank or totally spurious data are gathered. This is because the standard table
has data for CODEs 32 to 127 only and the graphics mode characters have
CODEs greater than 127; so the program is looking in the wrong place, beyond
the end of the table of data! The table in ROM immediately precedes the start
of RAM, the Random Access Memory used for temporary storage, so the pro-
gram will be looking at the display me that occupies the first 6K of RAM.
Block Graphics
The data for the block graphic set are not stored in ROM but instead are
calculated from the CODE of the character . Each block graphics character has
four quarters that are each represented, as on or off, by one of the last four
binary digits of the CODE number. In a block graphics character the first four
lines of eight pixels are identical, and the same is true of the bottom four lines.
.10001.1.01. = CODE ( ...... )
1.000X X X X = 8LOCK GRAPHICS
CHARACTE~: •
XXXX 1. 1. 0 1 =
>:----
FOUR CORNERS
''''HARACTER
OF
.- -- - -- -- -- ---- < <, --------: -- •
1111 1111 0000 1111
00001111 = VALUE FOR TOP 4 LINES
11111111 = VALUE 80TTOM 4 L J:NE :S
00001111
00001111
00001111
00001111
11111111 =
11111111
1111111.1
11111111
Figure 5.2
86 Advanced Graphics with the Sinclair ZX Spectrum
Provided we know that a character is a block graphic, we can generate two pieces
of data and use each four times to create the block. An example of how this is
achieved is shown in figure 5.2.
The block graphics set can be used to give medium -resolution graphics of 64
by 44 quarter blocks. Using this idea we can build up quite complicated two-
colour pictures very rapidly. The picture below (figure 5.3) was produced by the
program given in listing 5.2. The program draws a small set of concentric circles
in the top corner of the screen, then calls the 'big pixels' routine. This routine
asks us to specify a character block that will be the top-left corner for our start
position. From this position, for five character blocks down and for eight across,
it stores the data from the display file in the array S. For each pair of lines down,
it takes the eight pieces of data across and converts them to BINary strings (see
line 1130 onwards). These strings are used to build up the BINary representation
of the CODE for a graphics character by taking a two-bit section from each string
in turn and prefacing it by the CODE for graphics character (line 1230).
Listing 5.2
10~ REM main program
110 LET big pi xels = 100~
120 FOR 1=1 TO 18 STEP 3: CIRCLE 32,156,1: NEXT I
130 GO SUB big pi xels
140 STOP
... •• .
•....:..--. ~
••
I .' I· ••-
0))
I
II.l(.r-__
I
I
11··_·-.- II I
r»
--••-. .-.-.
I. I .1
• ••
Figure 5.3
The graphics characters generated by the above program are printed on the
screen and form an exact image of whatever was in the 5 by 8 rectangle of blocks
specified. This image has been scaled in each direction by a factor of 4, so that it
now covers 20 by 32 blocks and each original pixel is represented by a square of
4 by 4 pixels .
With the COPY facility and the ZX printer listings, four times the normal
width and depth can be produced by using the 'big pixels' routine 16 times and
then joining together the separate pieces of listing.
Exercise 5.2
Write a routine that produces a listing of itself by COPYing each 5 by 8 section
as 'big pixels' to the printer.
User-defmed Graphics
The third set of characters is the User-defined Graphics set (CODEs 144 to 164) .
Another table of data for this set is held in the last 168 locations at the end of
memory. When the machine is turned on, the 21 characters from "A" to "U" are
copied into this table. These characters are accessed from the keyboard in graphics
mode by typing the letters "A" to "U". The data in the table for these characters
can be changed by the user , so that any required character can be displayed
directly from the keyboard . There is a built-in function USR, which when given
a string argument ("A" to "U"), will give us the address of the first piece of data
for the equivalent graphics character. The address of the start of this table is
stored in the system variable UDG (see page 175 of the Spectrum BASIC Hand-
book (Vickers, 1982)). The CODE of the graphics character minus 144 and
multiplied by 8 gives the relative position of the first of the eight pieces of data
for that character in the table . The USR function calculates this position and
adds it to UDG to get the absolute start address of the character in the store.
88 Advanced Graphics with the Sinclair ZX Spectrum
UDG can be altered so that fewer characters are available, say only those from A
to H, leaving more room for programs, although this is usually unnecessary unless
you have only 16K of RAM available. To illustrate the use of this function to
alter a graphics character, type in the following command
Now if we enter graphics mode and type "A", we see that two extra pixel
dots have appeared above the character "A". The program in listing 5.3 allows
you to redefine all eight BINary VALUEs that represent a graphics character.
The program then simulates graphics mode , allowing you to type user-defmed
graphics characters on the screen.
Listing 5.3
10 INPUT "CHARACTER TO BE REDEFINED",US: IF US="" THEN GO TO 10
20 IF US < "A" OR us > "U" THEN GO TO 10
30 LET MEMORY = USR us: CLS
4~ FOR I = 0 TO 7
5~ INPUT ("VALUE" + STRS I +" = BIN ");LINE BS
60 LET VALUE = VAL ("BIN" + BS)
70 POKE MEMORY + I,VALUE
80 IF LEN BS < 8 THEN LET BS = "0" + as: GO TO 80
90 PRINT as: NEXT I
10rJ PRINT AT 10,0;"TYPE CHARACTERS OR ENTER"
110 REM simulate graphics mode for letters A to U.
110 LET R = 12: LET C = 0
120 PRINT AT R, C; FLASH 1; "G"
130 IF INKEYS 0 "" THEN GO TO 130
1~ LET AS = INKEYS: IF AS = "" THEN GO TO 140
149 REM if 'enter' is pressed restart program.
150 IF AS = CHRS 13 THEN GO TO 10
160 IF AS < "a" OR AS > "u" THEN GO TO 130
169 REM convert character to equivalent user-defined graphic and print.
170 LET AS = CHRS (CODE AS + 47)
18111 PRINT AT R,C;AS
189 REM move fake cursor poi nters.
190 LET C = C + 1: IF C = 32 THEN LET C = 0: LET R = R + 1
: IF R = 22 THEN LET R = 12
20rJ PRINT AT R,C; FLASH 1;"G"
210 GO TO 130
Exercise 5.3
Use listing 5.3 to generate a graphics character consisting of a chequer-board
pattern of pixels instead of completely filled areas. Experiment with this using
various PAPER and INK colours in order to make new colours , for example, red
and yellow give orange.
the screen very quickly compared to the length of time it would take to PLOT
or DRAW the same pattern. Listing 5.4 shows how this can be done using user-
defined characters "A" to "D" (the darker characters on the listing denote
characters in graphics mode). When these graphics characters have been defined
then they, rather than the corresponding alphabetic graphics character, will
appear in program listings.
Listing 5.4
20~ REM main program
209 REM fill screen with a diagonal pattern of first four graphics characters.
21~ OVER 0
220 INPUT" INK ";1: INK I
230 INPUT " PAPER "; I: PAPER I
240 LET A$ = "ABCD": LET B$ = "BCDA"
250 LET C$ = "c DAB " : LET 0$ = "DABC"
260 FOR J = 0 TO 7
270 FOR I = 0 TO 4
280 PRINT AT I*4,J*4;A$
290 PRINT AT 1*4 + 1,J*4;B$
30~ PRINT AT 1*4 + 2,J*4;C$
31~ PRINT AT 1*4 + 3,J*4;D$
320 NEXT I
330 PRINT AT I*4,J*4;A$
340 PRINT AT 1*4 + 1,J*4;B$
350 NEXT J
360 STOP
Listing 5.5
REM values whfch must be placed in CHARS to use each alternative set.
for 16K machines these numbers should be,
15360,29271,30~39,30807,31575,32080 (see Appendix).
5 FOR 1=1 TO 6: READ SCI): NEXT I
: DATA 15360,62039,62807,63575,64343,64848
6 LET S =1 : GO SUB 17
REM check set 1 is being used and print menu.
7 CLS : PRINT AT 2,2;"*** CHARACTER GENERATOR ***"
8 PRINT AT 5,6; PAPER 5;"1"; PAPER 7;" PRINT ALL SETS"
9 PRINT AT 7,6; PAPER 5; "2"; PAPER 7;" ••• PRINT ONE SET"
10 PRINT AT 9,6; PAPER 5;"3"; PAPER 7;" ••• EDIT CHARACTER"
11 PRINT AT 11,6; PAPER 5;"4"; PAPER 7;" COpy SET TO SET"
12 PRINT AT 13,6; PAPER 5;"5"; PAPER 7;" SAVE SET"
13 PRINT AT 15,6; PAPER 5;"6"; PAPER 7;" LOAD SET"
14 PRINT AT 17,6; PAPER 5;"7"; PAPER 7;" RUN YOUR PROGRAM"
REM wait for valid option.
15 INPUT ''WHICH OPTION ";OP: IF OP < 1 OR OP > 7 THEN GO TO 15
REM jump to appropriate rout ine.
16 GO TO G(OP)
REM most subroutines are located at start of program for efficiency.
REM routine to change to set S by alter ing chars to point to new table of data.
17 LET HI = INT (S(S)/256): LET LO = S(S) - HI*256
18 POKE 23606,LO: POKE 23607,HI: RETURN
REM general routine to wait before clearing screen and returning to menu.
19 LET S = 1: GO SUB 17: INPUT "PRESS ENTER TO RETURN TO MENU"; LINE A$ RETURN
REM clear binary characters array .
20 FOR I = 1 TO 8: LET ZS(I) = " 0 ~ ~~0~~~" : NEXT I: RETURN
REM input a valid set number and give upper and lower bounds for characters.
21 INPUT "WHICH SET ";S : IF S < 1 OR S > 6 THEN GO TO 21
22 LET A = 32: LET B = 127: IF S = 6 THEN LET A = 65: LET B = 85
23 RETURN
REM input a valid character for chosen set.
24 INPUT "WHICH CHARACTER ";C$: LET C = CODE CS
: IF C < A OR C > B THEN GO TO 24
25 RETURN
REM draw eight by eight block grid at horizontal position altered by N.
26 LET NN =64 + N: FOR I = 0 TO 8
27 PLOT 1*8 + NN,64: DRAW 0,64
28 PLOT NN,I*8 + 64: DRAW 64,0
29 NEXT I: RETURN
REM produce graphical equivalent of binary string array ins ide grid.
30 FOR I = 1 TO 8: FOR J = 1 TO 8
31 LET P = 7: IF N = 0 AND Z$(I,J) = "1" OR N = 96 AND T$(I,J) = "1"
THEN LET P = 4
32 GO SUB 48: NEXT J: NEXT I: RETURN
REM overprint cursor on a square in the grid.
33 PLOT X,Y-2: DRAW 0,4
34 PLOT X-2,Y: DRAW 4,0
35 RETURN
REM double a corner of a character i nt o a temporary array T$ and save it.
36 INPUT "WHICH CORNER ";C: IF C < 1 OR C > 4 THEN GO TO 36
37 FOR I = 1 TO 8: LET rscn = "~~~~~~~~": NEXT I: GO TO 36 + C*2
38 FOR I = 1 TO 4: FOR J = 1 TO 4: IF Z$(I,J) = "1"
THEN LET TI = 2*1: LET TJ = 2*J: GO SUB 46
39 NEXT J: NEXT I: RETURN
40 FOR I = 1 TO 4: FOR J = 5 TO 8: IF Z$(I,J) = "1"
THEN LET TI = 2*1: LET TJ = 2*(J - 4) : GO SUB 46
41 NEXT J: NEXT I: RETURN
42 FOR I = 5 TO 8: FOR J = 1 TO 4: IF Z$(I,J) = "1"
THEN LET TI = 2*(1 - 4): LET TJ = 2*J: GO SUB 46
43 NEXT J: NEXT I: RETURN
Character Graphics on the ZX Spectrum 91
44 FOR I = 5 TO 8: FOR J = 5 TO 8 : IF Z$U ,J) = "1 "
THEN LET TI = 2*0 - 4): LET TJ = 2*(J - 4): GO SUB 46
45 NEXT J: NEXT I: RETURN
REM subrout ine to convert one pixel i nt o four pixels i n doubled array.
46 LET T$(TI,TJ) = "1": LET T$(TI-1,TJ) = "1 ": LET T$(TI,TJ-1) = "1"
: LET T$(TI-1,TJ-1) = "1": RETURN
REM overprint the identifying labels at the corners of the grid.
47 PRINT AT 5,7;"1 ": PRINT AT 5,16;"2": PRINT AT 14,7;"3"
: PRINT AT 14,16;"4" : RETURN
REM subroutine to print a coloured block in a square of the grid.
48 PRINT AT I+5,J+7+N/8; PAPER P;" ": RETURN
REM display doubled character in a grid on the right and save character.
49
LET N = 96: GO SUB 26: GO SUB 30
GO SUB 21 : GO SUB 24: L ET 0 = S(S) + C*8 - 1
50
FOR I = 1 TO 8: POKE 0 + I,VAL ("BIN" + UU»: NEXT I
51
52
LET N = 0: CLS: RETURN
REM copy ref l e ct i on of Z$ array about x-axis into T$ array.
53 FOR I = 1 TO 8: FOR J = 1 TO 8
54 LET T$(9-I,J) = Z$(I,J): NEXT J: NEXT I : GO SUB 59: RETURN
REM copy rotat ion of Z$ array into T$ array.
55 FOR I = 1 TO 8: FOR J = 1 TO 8
56 LET T$(9-J,I) = Z$(I,J): NEXT J: NEXT I: GO SUB 59: RETURN
REM copy reflect ion of Z$ array about y- axis into T$ array.
57 FOR I = 1 TO 8: FOR J = 1 TO 8
58 LET T$(I,9-J) = Z$(I,J) : NEXT J: NEXT I: GO SUB 59: RETURN
REM routine to copy T$ array back i nt o Z$ array.
59 FOR I = 1 TO 8: FOR J = 1 TO 8
60 LET Z$(I,J) = T$(I,J): NEXT J: NEXT I: RETURN
REM start of option hand l ing routines.
REM pri nt out all five complete character sets and user-defined graphics set.
61 CLS: FOR S = 1 TO 5: GO SUB 17
62 PRINT AT S*4 - 4,0;
63 FOR C = 32 TO 127: PRINT CHR$ C; : NEXT C
64 NEXT S: GO SUB 17: PRINT AT 20, 0;
65 FOR C = 65 TO 85 : PRINT CHR$ C;: NEXT C
REM use standard routine to wait then back to menu.
66 GO SUB 19: GO TO 7
REM print out one set and give the charcters with which they correspond.
67 CLS: GO SUB 21: LET SS = S
68 FOR C = A TO B: PRINT PAPER 6;CHR$ C;"=";: LET S = SS: GO SUB 17
69 PRINT CHR$ C;: LET S = 1: GO SUB 17: PRINT" ";: NEXT C
REM return to menu.
70 GO SUB 19: GO TO 7
REM option three, editting a character, clear screen and Z$ binary array.
71 CLS: GO SUB 20
REM which set and character, calculate data pos ition f or character.
72 GO SUB 21: GO SUB 24: LET 0 = S(S) + C*8 - 1
REM get ei ght bi nary numbers f rom table of data.
73 FOR 1=1 TO 8: LET BO) = PEEK (0 + 0: NEXT I
REM convert each number into a binary string of eight dig its.
74 FOR I = 1 TO 8: LET T = BO)
75 FOR J = 1 TO 8
76 IF T >= P(J) THEN LET Z$(I,J) = " 1" : LET T = T - P(J)
77 NEXT J: NEXT I
REM draw a gr id and put green blocks in squares to represent binary ones.
78 GO SUB 26: GO SUB 30
REM i nitialise cursor position i n pi xeLs and gr id reference values.
79 LET X = =
68: LET Y 124: LET 1= 1 : LET J = 1
REM overprint cursor on square.
80 GO SUB 33
92 Advanced Graphics with the Sinclair ZX Spectrum
=
REM i f you type 5+6 then spectrum sets S 11 so load sets 5 and 6 toge ther.
115 IF S = 11 THEN LOAD N$ CODE (S(5) + 256),168 + 768 : GO TO 118
REM load user defined set.
=
116 IF S 6 THEN LOAD N$.CODE (S(6) + 512),168 : GO TO 118
117 LOAD N$ CODE (S(S) + 256),768
REM return to menu.
118 GO SUB 19: GO TO 7
The CHARACTER GENERATOR routines are intended to take all the hard
work out of preparing and using defined characters : they allow you to edit and
manipulate characters, use alternate character sets and graphics characters, save
and reload defined chracters and immediately test your own programs. The
routines are designed for the 48K Spectrum to sit in the bottom part of the
memory (lines 1 to 118) , followed by your own program starting after line 118,
followed by the renumber and delete programs (from chapter 13) starting at line
9900 . If you have the 16K version, then this program should be run independ-
ently of other routines - see Appendix A.
The program first offers a choice of seven options, which we will look at in
turn.
(1) The first option is PRINT ALL SETS. This will print the normal character
set (set 1), followed by the four alternate character sets (2 to 5), followed by the
user-defined graphics set (set 6). The last few characters of set 5 will contain
.spurious data , a remnant of the GO SUE stack, which has been moved out of
harm's way.
(2) The second option is PRINT ONE SET. These are identified with the num-
bers 1 to 6 as follows: 1 standard set - cannot be changed; 2 to 5 alternate sets
- may temporarily replace standard set; 6 user-defined graphics set - always
available. On selecting one of these numbers the screen will display each character
of the standard set with = after it, both on a yellow background , followed by the
equivalent character of the alternate set. For set 6 this will be produced only for
the characters "A " to "U", and it will show the characters available when using
these keys in graphics mode .
(3) Option three is the editor. This is the most complicated option and has a
large set of commands accessed by typing their initial letter. First, though, you
will be asked with which set and which character you wish to start. If you want
to start with a blank grid of eight by eight pixels, use set 1 and the space charac-
ter . (To use the quote marks character it will be necessary to type it twice - see
page 47 of the Spectrum BASIC Handbook (Vickers, 1982)).
You will now be in edit mode and the character you have chosen will be
displayed as green blocks in an 8 by 8 grid. The cross in the top-left corner is the
edit cursor. The cursor is controlled by the standard cursor keys ("5" , "6", "7",
"8") either with or without the shift key.
The first two commands are PLOT or OFF, which change a square in the
character grid. Simply move the cursor over the square and press either "P" or
"0" .
94 Advanced Graphics with the Sinclair ZX Spectrum
"P" makes the square green, equivalent to a binary on or one INKed-in pixel,
and "0" erases the square to white.
The next three commands all specify transformations similar to those per-
formed on two-dimensional objects in chapter 4 . ROTATE turns the character
through 90 degrees anti-clockwise about its centre . X-AXIS reflects the character
about the horizontal axis and Y-AXIS reflects the character about the vertical .
These commands can be used to create text sets for any orientation.
Finally we have MERGE, SAVE and DOUBLE. MERGE allows any character
to be merged, into the grid, on top of what is already being edited. This is very
useful for creating foreign language sets ; for example, by placing a slash through
an 0 for Scandinavian languages, or by adding accents for French, etc . to letters.
The SAVE command asks in which set we wish to save the newly created charac-
ter , and to which standard character is it equivalent. If set 1 is specified the
character will be lost , since set 1 is held in the Read Only Memory of the
Spectrum: this can be useful to dispose of any unwanted grids created by mis-
take. DOUBLEis a powerful feature that allows you to take any quarter of the
character you are editing, create a double-size copy of the quarter and SAVE it
immediately as a single character . This option does not affect the character you
are editing, so all four corners may be copied in succession into four different
characters within the same editing stage. This feature can be used to create
characters of double, quadruple or larger size with ease.
(4) COpy SET TO SET is the fourth option available and can be used to make
copies of a complete set for subsequent manipulation, or simply to move one of
the alternate sets around in memory . Sets 2 to 5 are stored at the end of memory,
in the locations preceding the user-defined graphics, with set 5 ending at the
location immediately before the user-defined set. The area of store used for sets
2 to 5 is reserved and protected from BASIC use by the command CLEAR 62294
in the 48K version (see page 168 of the Spectrum BASIC Handbook (Vickers,
1982)).62294 is one less than the first location in the table for set 2. In order to
protect only the areas for sets I onwards, we need to evaluate S(I) + 255 and
place this number in the CLEAR command at the start of the program. S(I) is the
value assigned to the system variable CHARS that enables PRINT to use set I.
These values are READ from the DATA statement at line 3 . Note the changes
for the 16K machine given in Appendix A.
(5) and (6) Option five allows character sets to be saved on tape and option six
for them to be reloaded. If, in reply to the question 'WHICH SET', we type a
number between 1 and 6, then that set is SAVEd or reLOADed . Should we type
5 + 6 then both sets 5 and 6 will be saved or loaded as a single unit.
To allow other programs to load and use alternate sets of characters, lines 112
to 117 should be copied from the 'CHARACTER GENERATOR' along with the
subroutine (at 17 and 18), which allows switching between sets. The array S and
its DATA from line 5 must also be included.
(7) The final option is RUN YOUR PROGRAM, which will transfer control to
the first line following the character generator or, if nothing is there, stop.
Character Graphics on the ZX Spectrum 95
Exercise 5.4
Experiment with various possible symmetries of characters and patterns for
placement of characters on the screen. Alter the program so that it tries all the
possible combinations of INK and PAPER colours within two nested FOR. . .
NEXT loops.
•
Figure 5.4
Exercise 5.5
Now create characters for dominoes using the 'CHARACTER GENERATOR'
from listing 5.5. This enables construction of a display similar to figure 5.4.
Exercise 5.6
Write a routine that copies one set to another but uses some of the editor
routines from the 'CHARACTER GENERATOR' to perform transformations on
each character as it is copied. Figure 5.5 shows a listing produced with a set that
has been ROTATED.
Let us now consider a complete program that has been developed using the
'CHARACTER GENERATOR' and subsequently separated from the develop-
ment system to stand alone. The 'MASTER MIND' program (listing 5.6) is shown
in mid-game in figure 5.6 and we can see immediately that an alternate character
96 Advanced Graphics with the Sinclair ZX Sp ectrum
Figure 5.5
set was used. This character set was created by DOUBL(E)ing the required
letters and numerals of set I and storing the top-left and top-right corners in the
capital letters of set 5 and the bottom corners in the lower case letters of set
5. The two sizes of peg were stored in the user-defined graphics set, which is
independent of the main set; hence they are always available whether we are
using set 1 or set 5. The DOUBLE letters were edit ed to smooth out their edges
and the combined sets 5 + 6 were stored on cassette tape as 'rnasterset' ,
Listing 5 .6
1 CLEAR 62294: INK 0: PAPER 7: BORDER 7
REM rout ines to allow use of alternate sets
REM remembe r to ma ke alterat i ons f or 16K machines as in l isting 5.5.
2 DIM S(6)
5 FOR I = 1 TO 6: READ SCI): NEXT I
: DATA 15360, 62039, 62807, 63575, 64343, 64848
6 LET S = 1 : GO SUB 17
7 GO TO 200
17 LET HI = INT (S(S) /256): LET LO = S(S) - HI*256
18 POKE 23606,LO: POKE 23607,HI: RETURN
112 CLS: PRINT AT 2,6;"LOADING CHARACTERS": LET N$ = "masterset"
113 PRINT AT 4,10;N$: LET S = 5 + 6
114 PRINT AT 21,HI;"Start tape.": PRINT AT 5,0;
115 LOAD N$ CODE (S( 5) + 256),168 + 768
118 RETURN
198 REM l oad characters and i nit ialise scores
199 REM arrays to hold Guess, Target and a copy of ta rget for checking.
200 GO SUB 112: LET MYSCORE = 0: LET SCORE = 0: DIM G(5): DIM T(4): DIM X(4)
209 REM use routine to draw disp lay.
210 RANDOMIZE: GO SUB 480
219 REM set up target colours and go i nt o guess loop.
220 FOR I = 1 TO 4: LET T(I) = INT (RND*6) + 1: NEXT I: LET GO = 0
229 REM next guess, if you have had s i x the n you lose.
Character Graphics on the ZX Spectrum 97
230 LET GO = =
GO + 1 : IF GO 7 THEN GO TO 390
239 REM i np ut routine for gu ess.
24~ GO SUB 780
249 REM print guess on board (A is graphics A).
250 PAPER 7
260 PRINT AT 1 + GO*3,11; INK G(1);"A "; INK G(2);" A";
INK GO);" A"; INK G(4); " A"
269 REM check guess aga i nst temporary copy of tar get.
270 LET NB = 0: LET NW = 0: FOR I = 1 TO 4: LET X(I) = T(I): NEXT I
279 REM look for e xact matches first, if found then cross off both pegs.
280 FOR 1 = 1 TO 4: IF GO) = X(I) THEN LET X(I) = 0 : LET NB = NB + 1
: LET G(1) = 7
29~ NEXT 1
299 REM check for r ight colour wrong position.
300 FOR 1 = 1 TO 4: FOR J = 1 TO 4: IF G(I) = X(J) THEN LET NW NW + 1
: LET X(J) = 0: LET GO) = 7
310 NEXT J: NEXT 1
319 REM set up array x with appropriate numbers of black and white pegs.
320 FOR 1 = 1 TO NB: LET X(I) = 0: NEXT I : FOR 1 = NB + 1 TO NB + NW
329 REM pad out array with non-visible green pegs.
330 LET X(1) = 7 : NEXT 1: IF NB + NW < 4 THEN FOR 1 = NB + NW + 1 TO 4
: LET X(I) = 4: NEXT 1
339 REM disp lay marker pegs on bo ard.
3~ PAPER 4: PRINT AT GO*3 + 1,2; INK X(1);"B"; INK X(2); "B";
350 PRINT INK X(3);"B"; INK X(4);"B";
359 REM i f you got i t all right then y ou win.
360 IF NB = 4 THEN GO TO 4~0
369 REM add one to computers score and loop back for next guess.
370 LET MYSCORE = MYSCORE + 1: PRINT AT 10,26; PAPER 6; INK 0;MYSCORE
380 GO TO 230
389 REM if you lose computer gets 10 more po ints.
390 LET MYSCORE = MYSCORE + 10: GO TO 410
399 REM i f you win you get 10 point s.
4~0 LET SCORE = SCORE + 10
~9 REM build up string for display at bottom.
410 LET IS CHRS 17 + CHRS 7 + "TARGET WAS" + CHRS 16 + CHRS T(1) + "A "
42~ LET 1$ IS + CHR$ 16 + CHRS T(2) + " A
430 LET 1$ IS + CHR$ 16 + CHR$ T( 3) + " A
44~ LET 1$ 1$ + CHR$ 16 + CHR$ T(4) + "A" + CHR$ 16 + CHR$ 0
450 LET 1$ 1$ + CHRS 6 + " PRESS ENTER TO CONTINUE
459 REM show t ar get and wait fo r 'enter' before restarting game.
460 INPUT (I$); LINE B$
470 GO TO 21 ~
479 REM draw boa rd.
480 OVER 0 : BORDER 6: PAPER 7: CLS: INK 0
489 REM use alternate set to print MASTER MIND in double size.
4~ LET S = 5: GO SUB 17
500 PRI~T AT 0,0;" ABCDEFGHIJKL ABMNOPQR"
510 PRINT AT 1 ,0;" abcdefgh ij kl abmnopqr"
519 REM pri nt out double s ize numbers.
520 PRINT AT 4,7;"ST": PRINT AT 5,7;"st"
530 PRINT AT 7,7;"UV": PRINT AT 8,7;"uv"
54~ PRINT AT 10,7;"WX": PRI~T AT 11,7;"wx"
550 PRINT AT 13,7;"Yl" : PRINT AT 14,7;"yz"
560 PRINT AT 16,7;"[\": PRINT AT 17,7;"{1 "
570 PRIN T AT 19,7;"Jt": PRINT AT 20,7;"}-"
579 REM wipe of sides of board in yellow.
580 PAPER 4: FOR 1 3 TO 20: PRINT AT 1,1;" ": NEXT I
59~ PAPER 6: FOR 1 = 0 TO 21: PRINT AT 1,24;" " : NEXT
600 PAPER 7: FOR 1 = 4 TO 19 STEP 3
609 REM print holes for pegs.
610 PRIr.-T AT I,11;"A A A A": NEXT
98 Advanced Graphics with th e Sinclair ZX Spectrum
MR5TER MIND
. YOU
• SCOR
HY
SCORE
4-
Figure 5.6
Character Graphics on the ZX Spectrum 99
The routines taken from the 'CHARACTER GENERATOR ' program have
been left with their original numbering so that they can be easily identified and
compared with the original. The display for the top part of the screen is built up
in easy stages from characters, colours and lines so that the role of each stage of
construction is straightforward and easily adjusted. The program uses also the
techniques of dynamic INPUT strings (which will be discussed in chapter 13) to
create the display for the lower part of the screen.
Exercise 5.7
Tidy up the 'MASTER MIND' program, giving it a structured appearance.
Increase its legibility with suitable variable names especially for routines. Adapt
the 'MASTER MIND' program to play against you . (Programs that do this have
occasionally been published in the past in computing magazines. Some are still
available; see Ahl , 1980 and Liffick, 1979).
Finally in this chapter, we give a simple illustration of how effective character
graphics can be in producing a high-quality display. The following short program
uses the 'chesspiece' characters from the cassette tape to display a chess board
(for example, figure 5.7 - also see cover) and to move pieces in response to
INPUTs. You can of course produce your own new chess set.
Listing 5.7
9 REM set up to use just sets 1 and 5 for 16K use CLEAR 31830: S5 = 31575.
10 CLEAR 64598: LET S5 = 64343: LET S1 = 15360
20 INPUT" LOAD CHARS? "iYS: IF YS = "y"
THEN LOAD "chesspiece" CODE S5 + 256,768
Figure 5.7
Exercise 5.8
Adapt the program in listing 5.7 to check for illegal moves and add facilities for
castling and en passant captures. If you have a lot of timell to spare then add
routines to make the computer play against you (see Liffick, 1979).
In the next chapter we shall consider how character graphics and our know-
ledge of two-dimensional geometry may be combined to form data displays.
Complete Programs
II. Listing 5.2 ('main program' and 'big pixels') . Data required: ROWand
COL where 0 ~ ROW ~ 16 and 0 ~ COL ~ 24 . Try (0,0) and (1, 1).
III. Listing 5.3. Data required: U$ and eight BINary numbers, each at most
eight bits long to redefine the graphics character specified by U$. Try "A"
and Ill, 111000 , 1010101 , 101, 11111001, 1001, 1111 , 10110;and "B",
"C" and "D", each with permutations of these eight numbers. Then press
keys "A" to "U" to display the equivalent graphics characters or press
ENTER to restart program.
IV. Listing 5.4: no data required. Program III must be used to create graphics
characters "A", "B", "c" and "D".
V. Listing 5.5: the CHARACTER GENERATOR. Read text for description
and example of use.
VI. Listing 5.6: requires 'masterset' from tape, or the generation of your own
character sets. To play MASTER MIND, type the number ("1" to "6")
corresponding to the colour of peg you wish to enter. Pressing "7" removes
last peg. When four pegs are in position, type " 1" to enter guess.
VII. Listing 5.7: requires 'chesspiece' from tape. Type coordinates of start and
destination squares of move. The coordinates of a square are given by a
capital letter ("A" to "H") followed by a number ("1" to "8"). Try "E2"
followed by "E4". To ACCEPTMOVE type "Y", to reject the move
type "N".
6 Diagrams and Data Graphs
More information is available to more people than ever before. Businessmen are
being overwhelmed by massive documents containing reams of statistics on every
subject from capital expenditure to market research. Sociologists bombard us
with figures on child development and the increasing percentage of octogenarians
in Bournemouth. Worst of all, computers are piling up printouts of dreary data
covering every topic from astrology to zoology. Obviously something must be
done! Computers have helped to create the problem and they can also help to
solve it. The data must be presented in a more digestible manner: as pie-charts,
histograms, scientific graphs or just plain diagrams. With the advent of desktop
computers the increasing sales of programs that produce these displays has made
this one of the major growth areas in computer graphics. In this chapter we shall
see how such diagrams can be constructed with ease, givenjust a few tools to
aid our draughtsmanship .
Cursors
KEYBORRD CONTROL5
FOR 'cursor' HOVEHENT
'" E Ri T ?
+-D F ~
C V B
ttl J, ~
Figure 6.1
Listing 6.1
57fJfJ REM cur sor
57fJ1 REM IN: PX,PY
57fJ2 REM OUT: PX,PY,ROW,COL
57fJ9 REM the rext Line is onLy executed on the first caLL to cursor.
5710 LET PX = 128: LET PY = 88: LET cursor = 5720: LET grid = 590fJ
5719 REM s t a r t of main cursor rout ine.
5720 INK 8: LET A = 1: LET FLAG = 1: OVER 1
5729 REM wait for key boa r d to be cLear be f or e Looki ng for comma nds.
5730 IF INKEY$ 0 ... . THEN GO TO 573fJ
5740 PLOT PX,0: DRAW 0,1 75: PLOT 0,PY : DRAW 255,0
5750 LET A$ = INKEY$: IF A$ = "" THEN LET A = 1: GO TO 575(;1
5760 IF CODE A$ > 64 AND CODE A$ < 96 THEN LET A$ = CHR$ (CODE A$ + 32 )
5770 PLOT PX,0: DRAW 0,175: PLOT 0,PY : DRAW 255,0
5780 IF (A$ = " e" OR A$ = "d" OR A$ = "c") AND PX >= A THEN LET PX = PX - A
5790 IF (A$ = " c" OR A$ = "v" OR A$ = "b" ) AND PY >= A THEN LET PY = PY - A
580fJ IF (A$ = "e" OR A$ = "r " OR A$ = "t") AND PX <= 175-A THEN LET PY = PY + A
5810 IF (A$ = " t " OR A$ = "g" OR A$ = "b" ) AND PX <= 255-A THEN LET PX = PX + A
582(;1 IF A$ = "f" THEN LET PX = 128: LET PY = 88
5830 IF A$ = " L" THEN GO SUB gr id
584(;1 IF A$ 0 CHR$ 13 THEN LET A = A + 1: GO TO 57 /.0
5850 LET COL = INT (PX/8): LET ROW = INT «175-PY)/8)
5860 IF FLAG = -1 THEN GO SUB gri d
5870 INK 0: OVER 0
5880 RETURN
Exercise 6.1
Write a 'main program' that calls 'cursor' and then prints a coloured square on
the screen at the specified block. It should also print out the ROW/COLumn
position of the block. Change the 'cursor' routine so that the standard cursor
keys ("5", "6 ", "7" and "8") can be used to move our cursor in character biock
steps about the graphics area.
Diagrams and Data Graphs 105
Attributes
We can extend the idea from exercise 6.1 to produce routines that change the
attributes of a given block or group of blocks without affecting their contents in
the display file. Listing 6.2 shows two routines that do this for the PAPER and
INK colours of a specified set of blocks .
Listing 6.2
40rJrJ REM paper
4010 GO SUB cursor: INPUT "WHAT COLOUR ";P: INPUT "No. OF BLOCKS (ROW*COL) "
i Ri "*" i C
411120 FOR I = ROW TO ROW + R - 1: FOR J = COL TO COL + C - 1
4030 PRINT AT I,J; PAPER P; INK 8; OVER 1; " ": NEXT J: NEXT I
41l141l1 RETURN
Exercise 6.2
Write a 'main program' that LISTs part of itself and then allows you to highlight
parts of the listing using different INK or PAPER colours. Write routines to be
placed at lines 4050 and 4150 , which alter the FLASH or BRIGHT attributes of
blocks. (Why will this mean adding FLASH 8 and BRIGHT 8 to the 'paper' and
'ink' routines?) . Combine all four routines into one named 'attribute'.
Having achieved simple interactive control over the appearance of our diagram,
we must now fill in the details of the picture. This requires routines for PLOT·
ting pixel points on the screen and for DRAWing lines.We could use program
Listing 6.3
420rJ REM poi nt
4210 INPUT "PRESS ENTER FOR CURSOR"; LINE AS
4220 GO SUB cursor: INPUT "OVER ( 1 OR III ) " ; 0 : INPUT "COLOUR " ; C
4230 PLOT INK C; OVER O;PX,py
4240 RETURN
listing 1.8, but it would be boring to go over every pixel in a line. Instead we use
two routines 'point' , which PLOTs individual pixels, and 'line' , in which we
simply specify the two end points of a line before joining them up. These two
routines are shown in listing 6.3.
Exercise 6.3
Write a 'main program' that allows you to use 'point' and 'line' to sketch a
picture of your Spectrum. Incorporate 'circle 1'or 'circle2 ' from listing 2.10 in a
'circle' routine ; where necessary, parameters, the centre and radius, are added
using the 'cursor'. Adapt your program from exercise 1.3 for use as a routine to
draw an n-sided polygon using the 'cursor' to enter the points. Add an extra
option to the 'line' routine to allow DRAWing of curved lines and then construct
a diagram similar to figure 1.1 Oa (it will be easier to use eight points around the
edge rather than twelve).
If you have a graphics pad then you can copy rough sketches from the pad
into the machine using an adjusted version of 'line' and 'point'. You should then
write programs to tidy up these pictures; that is, straighten lines and smooth out
curves.
Having spent some time and effort drawing and colouring a pretty picture, we
might wish to SAVE it for future reference. Another essential requirement is to
be able to LOAD a picture drawn by another program, and then alter it. The
two routines 'save' and 'load ' that make this possible are shown in listing 6.4.
Listing 6.4
Exercise 6.4
Run the program in listing 3.1 (or LOAD figure 3.1 if you have it stored) and
then change the display to green INK on black PAPER. Furthermore, there are
similar statements in both routines 'save' and ' load'. To save space , combine
'save' and 'load' into a single routine.
Diagrams and Data Graphs 107
Labels
Listings 6.1 to 6.4 form the basis of our diagram construction package, but as
yet we have not put any labels on the diagrams. This is achieved with the 'label'
routine, listing 6.5 . Note that in this routine we make references to routines not
yet written . This is a typical situation given the structured modular approach
adopted throughout this book. In practice this means that whenever we come up
against a problem that is too large to tackle immediately, we simply give it a
name and deal with it later. In 'label' we decide that character strings drawn
using symbols from set 5 will be printed vertically. For reasons explained later,
we also define a special prefix (7 :) that indicates that the string of characters for
a label is to be printed in narrow characters using a special routine 'thin'. We also
assume that there is a routine named 'set' that enables us to switch between
character sets.
Listing 6.5
5400 REM Labe L
5410 GO SUB cursor: INPUT "SET:STRING ";AS: IF AS = "" THEN GO TO 5410
5420 INPUT "COLOUR ";CS: INK VALe"0" + CS)
5430 IF AS( TO 2) = "1:" THEN PRINT AT ROW,COL;AS(3 TO ): GO TO 5510
5439 REM che ck for spec iaL set numbers, set 5 i s pri nted verticaLLy.
5440 IF A$( TO 2) <> "5:" THEN GO TO 5470
5450 LET S = 5: GO SUB set : LET A$= AS(3 TO )
5460 FOR I = 1 TO LEN A$~ PRINT AT ROW-H1,COL;A$(I): NEXT I: GO TO 5510
5469 REM se t 7 specifies that the thin routine shouLd be used for pr inting.
5470 IF A$( TO 2) <> "7:" THEN GO TO 5490
5480 LET A$ = A$(3 TO ): GO SUB thin : GO TO 5510
5489 REM check vaLid number is given.
5490 LET S = VAL A$(1): IF S > 6 OR S < 1 OR A$(2) <> ":" THEN GO TO 5510
5499 REM pri nt out st ri ng in appropri ate set.
5500 GO SUB set: PRINT AT ROW,COL;A$(3 TO )
5511'1 LET S = 1 : GO SUB set
5520 INK 0: RETURN
Data Graphs
Listing 6.6
Special Characters
To complete our preparations for the 'label' routine , we need to create a set of
ROTATED characters, which is placed in set 5 for use in writing vertical labels.
This is done in the routine 'create' (listing 6.7), which uses some of the techni-
ques from the CHARACTER GENERATOR to copy a rotated version of set I
into set 5.
Listing 6.7
50~~ REM create/character s
50~9 REM create chara cter s for histogram routine.
501~ DIM PCB): DIM 0(3): LET 0(1) = 15: LET 0(2) = 255: LET 0(3) = 240
5020 FOR I = 0 TO 6: FOR J = 0 TO 7
503~ LET P(1) = USR "A" + 1*8 + J: LET P(2) = USR "H" + 1*8 + J
5040 LET P(3) = USR "0" + 1*8 + J
5050 FOR K = 1 TO 3: POKE P(K),0: NEXT K
5060 IF J > I THEN FOR K = 1 TO 3: POKE P(K),O(K): NEXT K
507~ NEXT J: NEXT I
5078 REM copy set 1 to set 5 with a rotation to get sideways characters.
5079 REM for 16K machines Q = 31831.
5080 DIM B$(8,8) : LET T = 256: FOR I = 1 TO 8: LET T = T/2: LET P(I) = T
: NEXT I: LET P = 15616: LET Q = 64599
5090 FOR I = 32 TO 127: FOR J = 1 TO 8 : LET B$(J) = "~~~~~~~~": NEXT J
510~ FOR J = 1 TO 8 : LET T = PEEK P: FOR K = 1 TO 8
511 ~ IF T >= P(K) THEN LET T = T - P(K): LET B$(9-K,J) = "1"
5120 NEXT K: LET P = P + 1 : NEXT J
513~ FOR J = 1 TO 8 : POKE Q,VAL( "BIN " + B$(J»: LET Q = Q + 1: NEXT J
5140 PRINT AT 10,10i"ROTATING "i CHR$ I
5150 NEXT I: CLS
5160 RETURN
520~ REM thin
5201 REM IN : ROW,COL,A$
5209 REM pri nt every other cha ra ct e r i n Left haLf of bLocks.
5210 LET S = 4: GO SUB set: OVER 1: LET TC = COL
5220 FOR 1=1 TO LEN A$ STEP 2 : PRINT AT ROW,TCiA$(I): LET TC = TC + 1 : NEXT I
5229 REM pr int other characters in between in r ight half of bLocks.
5230 LET S = 3: GO SUB set: LET TC = COL
5240 FOR 1 =2 TO LEN A$ STEP 2 : PRINT AT ROW,TCiA$(I) : LET TC = TC + 1 : NEXT I
5250 LET S = 1 : GO SUB set: OVER 0
5260 RETURN
Before generating the rotated characters, the 'create' routine initially redefines
the user-defined graphics set as three sets of seven characters , see figure 6.2, that
allow either the left or right halves, or all of a character block, to be INKed in
from the bottom up . These characters may be used, by OVERprinting, to obtain
horizontal bars of varying thicknesses on the screen; but they are primarily for
use in the 'histo'gram routines that follow.
For the remainder of this chapter we shall discuss some common types of
data display . The vast number of variations on each of the major themes of
graphical display make it impractical to discuss all possibilities. We shall, there-
fore , consider in detail just a few examples from each of the three main types of
display : histograms, pie-charts and graphs. From these simple examples it should
110 Advanced Graphics with the Sinclair ZX Spectrum
USER-DEFINED GRAPHICS CHARACTERS
..
FOR USE UITH HISTOGRAMS
AI B. .... C. D. E. F_ G
n. t<- L- t1- N_
~ Pw f4 R. S. T_ U
Figure 6.2
Histograms
Exercise 6.5
As variations on the standard 'histo ' routine we can write replacement routines
that can be MERGEd as required . Write a routine that calculates the position for
pairs of bars, where the bars within a pair are separated by half a block, but pairs
are separated from neighbouring pairs by at least one block . Use this to construct
diagrams similar to figure 6.4.
An example of a replacement 'histo' routine is given in listing 6.9. In this
version of 'histo' (/type2) the calculation of the width of the bars is altered to
provide whole character block values for X and GAP. Two data values are
requested for each bar, specifying the MAXimum and MINimum height range of
Diagrams and Data Graphs 111
Listing 6.8
. AVERAGE MONTHLY
100
RAINFALL in °E GHAH
.
... 7 5
...
f 5 0 !
_ ;J t'".::;
'- '--
oJ F H R H J J R 5 -I t ~
H ont h S
Figure 6.3
3 0 0
100
1 st 2 n d 3,d
YERR AT C O L LEG -
Figure 6.4
Diagrams and Data Graphs 113
the bar. By simply OVERprinting the bars, charts like figure 6.5 , of the monthly
temperature variation in Egham, can be produced. In order to understand this
fully, as well as other programs in the book, it is a good idea to scatter PRINT
statements throughout the listings, so you can follow the logic of the algorithms
as they are executed. A very nice feature in BASIC is that you can add extra
Listing 6.9
10~~ REM h lsto/type2
10~9 REM set pointer to subroutine wh ich pri~ts bars.
1010 LET bar = 1320: DIM 0$(2,3): LET 0$(1) = "MAX": LET 0$(2) "MIN"
1019 REM find scaLe and draw axes.
1020 INPUT "RANGE OF VERTICAL "iYBi" TO "iYT
1030 IF YB >= YT TH EN GO TO 1020
1040 LET YSCALE = 128/(YT - VB)
1050 PLOT 47,152: DRAW 0,-129: DRAW 201,0
1059 REM LabeL verticaL axis.
1060 LET YDIF = (YT - YB)/4: LET TICK = YB
1070 FOR I = 1 TO 5: LET TK = INT (TICK + 0.5)
1080 LET Y = 32*1 - 8: PLOT 47,Y: DRAW -3,0: LET ROW = INT «176 - Y)/8)-1
1090 LET A$ = STR$ TK: IF LEN A$ > 3 THEN LET A$ = A$( TO 3)
: IF TK > 999 OR TK < -99 THEN LET A$ = "***"
110~ LET COL = 1 : IF TK >= 0 THEN LET COL = COL + 1
1110 IF AB S TK < 111l TH EN LET COL = COL + 1
1120 IF ABS TK < 111l~ THEN LET COL = COL + 1
1130 PRINT AT ROW,COLiA$: LET TICK = TICK + YDIF
1140 NEXT I
1149 REM f ind number of bars required and caLcuLate an integer width fo r them.
1150 INPUT "No. OF BARS "iNB: OVER 1
1160 LET GAP = INT (13/NB): LET X = INT «26 - GAP*NB)/NB)
117~ LET GP = (25 - NB*X - (NB - 1 )*GAP): LET GP = INT «GP + 1 )/2)
1179 REM position coLum n poi nter at first bar then repeat Loop for each bar.
11E0 LET COL = 6 + GP: FOR I = 1 TO NB
: INPUT ("COLOUR FOR BAR " + STR$ I + ": ")iC: INK C
1189 REM Loop to i r put maximum and minimum vaLues for bar.
1190 FOR 0 = 1 TO 2: LET 1$ = "DATP. " + 0$(0) + " FOR BAR" + STR$ I + " "
: INPUT O$)iD: LET W = X
120fl IF D <= VB THEN LET D = 0: LET H = 1: GO TO 1220
1209 REM cal cuLate the number of whoLe bLocks of height for ba r.
1210 LET D = D - VB: LET H = INT (D*YSCALE + 0.5)
1220 LET IH = It\T (HI 8): LET M$ = ""
1229 REM construct string to height for bar.
1230 FOR J = 1 TO IH: LET 1'$ = M$ + CHR$ 143
1239 REM add spEciaL character for remaining he ight.
1240 NEXT J: LET RH = H - IH*8: IF RH = 0 THEN GO TO 1260
1250 LET M$ = M$ + CH ,$ (158 - RH)
1259 REM start at same coLumn for both bars in pair.
1260 IF 0 = 1 THEN LET OCOL = COL
1 27~ IF 0 = 2 THEN LET COL = DCOL
1279 REM output requirEd number of whoLe bLobk width bars.
1280 IF W >= 1 THEN LET A$ = MS: GO SUB bar: LET W = W- 1
: LET COL = COL + 1: GO TO 1280
1290 LET COL = COL + GAP: NEXT 0: NEXT I
130~ INK 11l: OVER 0: RETURN
STOPs to a program, interrogate the variable values, and then CONTinue , with -
out affecting the status of the program. This is a very useful tool for debugging
a program, as well as an aid to understanding a listing .
MONTHLY TEMPERATURE
30
RANGE in EGHAH
•
..
IIII
u 20
II
1.-
I
(JIl
.'
10
III •I
c
.-
u 0
1 '0
Figure 6.5
Exercise 6.6
Try using a different colour PAPER when OVERprinting the bars to produce
two-coloured bars. Be careful when using data values giving scale heights with
less than eight pixels difference!
Problems will occur when using two different colours for one bar (exercise
6.6). If the MAXimum and MINimum values both fall in the same character
block, then three colours , these two plus the background, will be required with-
in one block. We may check whether problems will occur by comparing the
number of character blocks in the string produced for the MINimum data with
that for the MAXimum data . If they are of equal length then the simplest way
of avoiding trouble is to truncate the MINimum string by removing the last
character. This will remove any problems with the colours on the display but
will be slightly inaccurate . This whole problem will be avoided if we make the
colour of the lower part of the bar the same as the background colour, thus
ensuring that the bar is accurately drawn with only two different colours in each
block .
There are many , many more variations possible ; for example , drawing bars
above and below a central line in order to display fluctuations in currency
exchange rates. The fundamental ideas we have introduced should enable you to
produce any histogram to your own specification .
Diagrams and Data Graphs 115
Pie-Charts
The pie-chart is a favourite with economists and biologists who delight in telling
us how big each slice of our capital expenditure cake is, or alternatively which
bacteria are eating it . The usual requirements of a pie-chart program are that it
should draw pies of variable radii , it must be possible to pull out slices of the pie
from the centre , and provision for filling in or cross-hatching these slices must be
made available. The 'pie'-chart routine given in listing 6.10 achieves the first two
Listing 6.10
2000 REM pie/ chart
2009 REM set pointers to routines.
2010 LET hatch = 2300: LET in = 2800
2019 REM aLL segments are i nput and totaLLed to ca LcuLat e anguLar scaLe.
2020 INPUT "No. OF SEGMENTS "iNB: INPUT "COLOUR " i C$ : LET C = VAL ("0" + C$)
: LET TOT = 0
2030 DIM D(NB): FOR I = 1 TO NB : INPUT ("DATA" + STR$ I + to: ")iDO)
: LET TOT = TOT + DO): NEXT I
2039 REM use cursor to specify centre of pie.
2040 INPUT "PRESS ENTER FOR CENTERING PIE"iLINE Y$
2050 GO SUB cursor: LET XC = PX: LET YC = PY
2060 INPUT "RADIUS <IN PIXELS) " iRAD
2070 LET ASCALE = 2*PI/TOT: LET A1 = PI/2
2079 REM any wedge may be puLLed out by moving the cursor away from the centre.
2080 FOR I = 1 TO NB
2090 INPUT "PRESS ENTER FOR CENTERING WEDGE"iLINE Y$
2100 LET PX = XC: LET PY = YC: GO SUB cur sor: LET ANG = ASCALE*D(I)
: LET A2 = A1 - ANG
2109 REM if wedge is to move out caLcuLate dispLacement aLong i t ' s bisector.
2110 IF PX = XC AND PY =YC THEN GO TO 2140
2120 LET A3 = A1 - ANG/2: LET DIST = SQR «PX - XC)*(PX - XC) +
(PY - YC)*(PY - YC»
2130 LET PX = INT (XC + DIST*COS A3 + 0.5): LET PY = INT (YC + DIST*SIN A3
+ 0.5)
2139 REM enquire whether hatching is required and what type.
2140 INPUT "HATCH (x,y,b,n) "iH$ : IF CODE H$ < 96
THEN LET H$ = CHR$ (CODE H$ + 32)
2149 REM i f hatching is wanted i nput gap size between Lines and offset.
2150 IF H$ <> "n" THEN INPUT "JUMP "iJUMP,"REM "iREM
2159 REM draw segment of pie.
2160 INK C: PLOT PX,PY: LET X1 = INT (RAD*COS A1 + 0.5)
: LET Y1 = INT (RAD*SIN A1 + 0.5): DRAW X1,Y1
2170 LET A2S = A2: LET ASTO = ANG: LET XA = PX + X1: LET YA = PY + Y1
2180 LET XB = INT (RAD*COS A2 + 0.5): LET YB = INT (RAD*SIN A2 + 0.5)
2190 FOR T = A1 TO A2 STEP -3/RAD
2200 LET X2 = INT (RAD*COS T + 0.5): LET Y2 = INT (RAD*SIN T + 0.5)
: DRAW X2 - X1,Y2 - Y1: LET X1 = X2: LET Y1 = Y2
2210 NEXT T: DRAW XB - X2, YB - Y2: LET X2 = XB: LET Y2 = YB
2220 DRAW -XB,-YB: IF H$ = "n " THEN GO TO 2250
2228 REM calL hatching routine if needed.
2229 REM if angLe is greater than half circle then do hatching in two parts.
2230 IF ASTO > PI THEN LET A2 = A1 - PI: LET XB = 2*PX - XA: LET YB = 2*PY -VA
: GO SUB hatch: LET XA = XB : LET YA = YB: LET A1 = A2: LET A2 = A2S
2240 LET XB = PX + X2: LET YB = PY + Y2: GO SUB hatch
2249 REM loop back for next segment.
2250 LET A1 = A2: NEXT I: RETURN
116 Advanced Graphics with the Sinclair ZX Spectrum
requirements by using the 'cursor' to centre the chart and INPUTting the
RADIUS in pixels. The individual data items are then INPUT and TOTalled,
and this total is used to establish an angular scale for the 'pie'-chart. Each slice is
centred with the 'cursor', with any displacement of the 'cursor' from the centre
of the 'pie' being treated as a distance along the bisector of the slice and not as
an absolute position. With each new section the 'cursor' reappears at the original
centre of the 'pie'. Figure 6.6 was generated using this routine . . .
o nership of
dwellings
in t.he U.K.
o ner-
occupied
Local
authority
Rented
Figure 6.6
Hatching
Hatching the area of a pie-slice involves the intersection of a line with the
boundaries of the slice. To make the calculations simpler we shall hatch using
lines only in the horizontal direction or only in the verical direction , or both.
Furthermore we hatch only 'pie's that subtend angles less than or equal to 1T
radians (180 degrees) at the centre . For obtuse angles the 'pie' is treated as two
pieces, the first subtending 1T radians at the centre . The 'pie' routine enquires
whether the hatching is to be horizontal (answer "x"), vertical (answer "y "),
both ways (answer "b") or neither (answer "n").
The pie sections we are considering are each bounded by two line segments
and a circular arc. We must find which part of a hatching line (if any) lies inside
this segment. Becausethe 'pie' does not subtend an angle greater than 1T radians
at its centre there are only four possibilities
(3) it may intersect the arc and one of the line segments
(4) it may intersect both line segments
The special cases where the line coincidently cuts the arc and a line segment
at the same point may be included in one of the above four possibilities. The
explanation of the hatching algorithm is given with reference to horizontal
hatching; the vertical follows in an equivalent manner . We first find the MAXi-
mum and MINimum y-values of points within the 'pie' section. Then we consider
all horizontal hatching lines with equations of the form Y = hJUMP + REM
between these limits (0 ~ REM ~ JUMP - I) . For each hatching line we calcu-
late the two points of intersection with the extended line segments and then
check whether their MU values lie between 0 and I; that is, whether the inter-
section is between the centre of the circle and the arc. Next we find the two
points of intersection of the hatching line with the complete circle containing
the arc, and then check whether they lie on the arc. From these we can find the
two points of intersection of the pie section and the hatching line, and these are
then joined. This whole process is programmed in listing 6.11 and an example of
its use is given in figure 6.7 . Note that to fill in a slice completely we simply set
JUMP equal to 1.
Listing 6.11
2300 REM hat ch
2309 REM if cross hatching is required then run hatch rout ine twice.
2310 IF H$ ="b" THEN LET H$ = " x": GO SUB 2320: LET H$ = "y"
: GO SUB 2320: LET H$ ="b" : RETURN
2319 REM set hatch ing var iabLes to controL direct ion of Lines.
2320 IF H$ = "y" THEN LET PZ = PX: LET PT = PY : LET ZA = XA: LET TA = YA
: LET ZB = XB: LET TB = YB
2330 IF H$ = "x" THEN LET PZ = PY: LET PT = PX: LET ZA = VA: LET TA = XA
: LET ZB = YB: LET TB = XB
234l<l DIM Z(3)
2349 REM find max. and min. coordinates for Lines wh ich pass through segment.
2350 LET T = PI/2: LET MAX = 0 : LET MIN = 0
2360 LET VAL = SIN A1: IF H$ = "x" THEN LET VAL = COS A1
2370 IF MAX < VAL THEN LET MAX =VAL
2380 IF MIN> VAL THEN LET MIN =VAL
2390 IF T > A1 THEN LET T = T - PI/2: GO TO 2390
2400 IF T < A2 THEN GO TO 2450
2410 LET VAL = SIN T: IF H$ = "x" THEN LET VAL = COS T
2420 IF MAX < VAL THEN LET MAX = VAL
2430 IF MIN> VAL THEN LET MIN = VAL
2440 LET T = T - PI/2: GO TO 24l<l0
2450 LET VAL = SIN A2: IF H$ = "x" THEN LET VAL = COS A2
2460 IF MAX < VAL THEN LET MAX =VAL
2470 IF MIN> VAL THEN LET MIN = VAL
2480 LET NEWMIN = INT (INT (RAD*MIN + 1)/JUMP)*JUMP + REM
2489 REM fo r Lines which crosses segment find i ntersections with radii and arc.
2490 FOR E = NEWMIN TO MAX*RAD STEP JUMP
2499 REM store i ntersect ion coordinate informat ion in array Z(1:4).
2500 LET IC 0 =
2510 LET DENOM = TA - PT: IF DENOM = 0 THEN GO TO 2540
2520 LET MU = E/DENOM: IF MU < 0 OR MU > 1 THEN GO TO 2540
2530 LET IC = IC + 1: LET Z(Ie> = PZ + MU*<ZA - pZ)
2540 LET DENOM = TB - PT: IF DENOM = 0 THEN GO TO 2580
2550 LET MU =E/DENOM: IF MU < 0 OR MU > 1 THEN GO TO 2580
118 Advanced Graphics with the Sinclair ZX Spectrum
FREQUENCY Crustaceans
OF
INVERTEBRATES IN A BRITISH POND
Figure 6.7
Graphs
ordinate axes that need not be fixed, and can cover a bewildering variety of
ranges for their scales. The method we use to decide on the placing of a particu-
lar axis is fairly standard: if zero should lie in the range of the graph then the axis
passes through that point, otherwise it lies on the edge of the graphics area,
closest to zero. Five TICKs are then placed along each axis and the scale value
at that point is written close to each TICK. The need for accuracy in scientific
graphs makes us wish to include as many characters as possible. As things stand
we can have only 32 characters across the screen and 22 up it, so this is where
'thin' comes in . We used the CHARACTER GENERATOR to create two sets of
thin characters that are half the width of normal characters : one set having
characters in the left half of the character block and the other set being in the
right half. When a string is to be printed as thin characters, the 'thin' routine
prints every other character of the string in the left-biased set and then OVER-
prints this, with the remaining characters in the right-biased set. This places two
thin characters in each block, giving us 64 print positions across the screen. The
numbers to be printed as 'thin' 'label 's still need to be converted into strings and
made consistent in length and/or decimal accuracy. This is achieved by the
routine 'number', which follows in listing 6.12 . Note that the routine 'thin' has
already been used in the 'label 'ling of figure 6.6.
Listing 6.12
300~ REM graph
30~9 REM symbol routine is used to mark data points on discrete graphs.
3010 LET symbol = 3500
3019 REM calculate scales and draw axes.
3020 INPUT "X GOES FROM "iXBi" TO "iXT: IF XT <= XB THEN GO TO 3020
3030 INPUT "Y GOES FROM "iYBi" TO ";YT: IF YT <= YB THEN GO TO 3030
304'!1 LET XSCALE = 1921(XT - XB) : LET YSCALE = 128/(YT - VB)
3050 LET XO = INT (-XB*XSCALE + 32.5): LET YO = INT (-YB*YSCALE + 24.5)
3059 REM if zero is not in range of graph move axis to appropriate edge.
3060 IF YT < 0 THEN LET YO = 153
3070 IF YB > 0 THEN LET YO = 23
3080 IF XT < 0 THEN LET XO = 224
3090 IF XB > 0 THEN LET XO = 31
3100 PLOT XO,23: DRAW 0, 128: PLOT 31,YO: DRAW 192, 0
3101 REM use th i n routine to print labe ls on axes with fou r figure accu racy.
3110 LET XDIF = (XT - XB)/4: LET YDIF = (YT - YB)/4
3120 LET X = XB: LET Y = VB: FOR J = 1 TO 5
3130 LET PX = INT «X - XB)*XSCALE + 32.5): LET PY = YO
3140 PLOT PX,PY-2: DRAW 0,4
3150 LET COL = INT (PX/8) - 1: LET ROW = INT «175 - PY)/8) + 1
3160 LET A = X: GO SUB number: GO SUB th in
3170 LET PY = INT «Y - YB)*YSCALE + 24.5): LET PX = XO
3180 PLOT PX-2,PY: DRAW 4,0
3190 LET COL = INT (PX/8) + 1: LET ROW = INT «175 - pn/8)
3200 LET A = Y: GO SUB number: GO SUB thin
3210 LET X = X + XDIF: LET Y = Y + YDIF: NEXT J
3220 INPUT "CONTINUOUS OR DISCRETE GRAPH "i D$: IF 0$ <> "c " AND 0$ <> "d"
THEN GO TO 3220
3230 IF 0$ = "d" THEN GO TO 3320
3239 REM i nput the function to be plot ted.
3240 INPUT " F(x) : y="; LINE F$
3250 LET X = XB : LET Y = VAL (F$) : LET OY = INT «Y-YB)*YSCALE + 24.5)
120 Advanced Graphics with the Sinclair ZX Spectrum
Exercise 6.7
Write an extended 'number' routine that allows you to specify the format of
the string to be returned. One way of doing this is to enter a string containing a
template for the number format; for example, the string '##.## #! could
specify a number with two digits before the decimal point and three decimal
places after it.
Exercise 6.8
Construct the thin 'sets' required for numerical labelling (these appear on the
cassette tape as 'thin3' and 'thin4'). Use them for 'label 'ling diagrams. Figure 6.8,
which was drawn using listing 5.2, will help you with your construction.
I) 1 2 3 .:I
6• ., 3 9
+ •-••
/.
Figure 6.8
Diagrams and Data Graphs 121
Figure 6.9
CHANGES IN pH VALUE OF
RIVER YATER OVER 24 HIs .
•. Z 5 y - - - - r - - - - r - - - - - , -_ _---.
,.
> 7.75
I
Q
1.501
'.1"S +------4-----.J--.----+--_ _ ~
~ ~ 1.2tIt 1.-
TIHE (Hou,s)
Figure 6.10
122 Advanced Graphics with the SinclairZX Spectrum
example of each type of diagram is given. Figure 6.9 shows a typical continuous
cosine curve and figure 6.10 shows discrete scientific data about the pH levels of
a river.
Exercise 6.9
It can be seen that the only requirement for a graph of this type is a set of co-
ordinates in ascending order of X, which are then joined up. This set can be
created in any manner: by a series of READ statements or by a multi-line
calculation in a subroutine. Instead of eVALuating the function string F$ we
could write a routine that is used every time we need to calculate a point on the
curve. Produce a routine that allows the graph of SIN X/X to be drawn , avoiding
the calculation SIN 0/0 .
Complete Programs
We group listings 6.6 ('main program', 'charload' , 'set' and 'query'), 6.1 ('cursor'
and 'grid'), 6.4 ('save' and 'load') under the name 'libdiag', and use it with 6.2
('paper' and 'ink'), 6.3 ('point' and 'line'), 6.5 ('label') and 6.7 ('create' and
'thin'): all found on the tape. Note the changes for the 16K machine mentioned
in appendix A.
III. 'libdiag' and listing 6.9 ('histo'/type2). Data required: for example
IV. ' libdiag' and listings 6.10 and 6.11 ('pie', 'hatch', etc.). Data required : for
example
'No . OF SEGMENTS' 3
'COLOUR' 0
'DATAI' 1
'DATA 2'2
'DATA 3' 3
Centre pie with cursor then 'RADIUS (IN PIXELS)' 75
Use cursor to centre wedge 'HATCH' (x , y, b, n) E.: 'JUMP'~: 'REM' ~
Use cursor to centre wedge 'HATCH' (x, y, b, n) y : 'JUMP' 1: 'REM' 0
Use cursor to centre wedge 'HATCH' (x, y, b, n) ~ - -
V. 'libdiag' and listing 6.12 ('graph' , etc.) . The questions posed by the program
are self-explanatory: like those above. .
7 Three-dimensional Coordinate
Geometry
Although these are three equations in three unknowns, we shall find that they are
interrelated (so-called linearly dependent) and so there is no unique solution
(naturally, since we are generating a general point on the line, not just one point).
These equations enable us to calculate two of the coordinates in terms of a third
(see example 7.1).
As with two dimens ions, this is not the only way of representing a line ; in fact
the second way that we introduce is possibly more useful. The general point on
the line is represented as a vector that is dependent on only one real number IJ.,
and is given as the vector sum of two scalar multiples of vectors
that is
like its counterpart in two -dimensions , P I is called a base vector and (P2 - PI)
a directional vector. Again we see the dual interpretation of a vector. A vector
can be used to specify uniquely a point in three-dimensional space, or it can be
considered as a general direction; namely, any line parallel to the line that joins
the origin to the vector (considered as a point). We can move along a line in one
of two directions, so we say that the direction from the origin to the point has
positive sense, and from the point to the origin negative sense. Hence vectors
126 Advanced Graphics with the Sinclair ZX Spectrum
d == (x, y, z) and -d == (-x, -y, -z) represent the same line in space but their
directions are of opposite senses. We define the length of a vector d == (x, y , z)
(sometimes called its modulus , or absolute value) as 1dl, the distance of the
point vector from the origin
So any point on the line p + ud is found by moving to the point p and then
travelling along a line that is parallel to the direction d, a distance Jl I d 1in the
positive sense of d if Jl is positive, and in the negative sense otherwise. Note any
point on the line can act as a base vector, and the directional vector may be
replaced by any non-zero scalar multiple of itself.
If the directional vector d == (x, y , z) makes angles Ox , Oy and Oz with the
respective positive x-direction,y-direction and z-direction, then the ratios
which means that d == (A x cos Ox, Ax cos Oy, A x cos Oz) for some A.
We know from the properties of three-dimensional geometry that
Hence A= I d I, and if the directional vector has unit modulus (that is, modulus
= A= 1), then the coordinates of this vector must be (cos Ox, cos 0y, cos 0z) ;
that is, A = 1. The coordinates of a directional vector given in this way are called
the direction cosines of the set of lines generated by the vector . In general, if
the direction vector is d == (x, y , z) then the direction cosines are
X y z)
( Tdi'Idi'1di
Example 7.1
Describe the line joining (1,2,3) to (-1 ,0,2), using the three methods shown
so far.
The general point (x, y , z) on the line satisfies the equations
Notice that equation 7.1 is -2 times the sum of equations 7.2 and 7.3. Thus we
need consider only these latter two equations, to get
Three-dimensional Coordinate Geometry 127
y = 2z - 4 and x = 2z - 5
so that the general point on the line depends only on one variable, in this case z,
and is given by (2z - 5, 2z - 4, z) . We easily check this result by noting that
when z = 3 we get (1,2 ,3) and when z = 2 we get (-1 ,0,2), the two original
points defining the line.
In vector form the general point on the line (depending on Jl) is
Again the coordinates depend on just one variable (Jl), and to check the validity
of this representation of a line we note that p(O) == (1,2,3) and p(l) == (-1,0,2).
If we put the line into base/directional vector form we see that
with (1,2,3) as the base vector and (-2, -2, -1) as the direction (which
incidently has modulus y(4 + 4 + 1) = y9= 3) . We noted also that any point on
the line can act as a base vector , and so we can give another form for the general
point on this line,p'
We can change the directional vector into its direction cosine form (-2/3 , -2/3 ,
-1/3) and represent the line in another version of the base/direction form
Naturally the same Jl value will give different points for different representations
of the line; for example, p(3) == (-5, - 4, 0) , p'(3) == (-7, - 6, -1) and p"(3) ==
(-1,0,2). The direction of this line makes angles of 131.81 degrees = cos"?
(-2/3),131.81 degrees and 109.47 degrees = cos" (-1/3) with the positive
x-direction,y -direction and z-direction respectively .
In order to calculate such an angle, first we introduce the operator· the dot
product or scalar product. This operates on two vectors and returns a scalar
(real) result. Thus
If p and q are both unit vectors (that is, in direction cosine form), and 8 is the
128 Advanced Graphics with the SinclairZX Spectrum
angle between the lines, then cos () = p • q (see chapter 3 for equivalent two-
dimensional relationship). In general, therefore, the angle between two direction-
al vectors p and q (we can assume they meet at the origin) is
cos
-I (P
IPT . IQT)
q ~
Definition of a Plane
The plane is the next object we must consider in three-dimensional space. The
general point x == (x, y, z) on the plane is given by the vector equation
n·x=k
where k is a scalar, and n is the directional vector of the set of lines that are
perpendicular to (or normal to) the plane (see example 7.2). If a is any point
on the plane then naturally n • a = k, and so by replacing k in the above
equation, we can rewrite it as
n • x = n • a or n· (x - a) = 0
This latter equation is self-evident from the property of the dot product, that
two mutually perpendicular lines have zero dot product. For any point x ==
(x, y, z) in the plane that is not equal to a, we know that (x - a) can be
considered as the direction of a line in the plane . Since n is normal to the plane ,
and incidently perpendicular to every line in the plane, then n • (x - a) = cos
(rr/2) = O.
Expanding the original equation of the plane with normal n == (nl, n2, n3),
we get the usual coordinate representation of a plane
Note that two planes with normals nand m (say) are parallel if and only if one
normal is a scalar multiple of the other; that is, n = Am for some A.
Suppose the line is given by b + /ld and the plane by n • x =k . Since the point of
intersection lies on both the line and the plane, we have to find the unique value
of /l (if one exists) for which
Three-dimensional Coordinate Geometry 129
n • (b + Jld) = k
Jl = (k - n • pd/(n • n)
from the equation above, and the distance of the point pz =Pi + un from Pi is
In particular, if Pi is the origin 0 then the distance of the plane from the origin
is I k I/ I n I. Furthermore, if n is a direction cosine vector, we see that the distance
of the origin from the plane is I k I, the absolute value of the real number k.
Example 7.2
Find the point of intersection of the line joining (1,2,3) to (-1 ,0,2) with the
plane (0, -2,1)' x = 5, and also find the distance of the plane from the origin.
b =(1 , 2, 3)
n =(0, -2,1)
d = (-1, 0, 2) - (1, 2, 3) =(-2, -2, -1)
n • b = (0 x 1 + - 2 x 2 + 1 x 3) = -1
n • d = (0 x -2 + -2 x - 2 + 1 x-I) = 3
hence the Jl value of the point of intersection is (5 - (-1 ))/3 = 2, and the point
vector is
The program given in listing 7.1 enables us to calculate the point of intersec-
tion (array P) of a line and a plane . The line has base vector B and direction D,
and the plane has normal N and plane constant K. Note, since we are working
with decimal numbers, and thus are subject to rounding errors, we cannot check
130 Advanced Graphics with the Sinclair ZX Spectrum
Suppose we have two lines b 1 + J,J.d 1 and b 2 + "Ad2 • Their point of intersection ,
if it exists (if the lines are not coplanar or are parallel then they will not inter-
sect), is identified by finding unique values for J,J. and "A that satisfy the vector
equation (three separate coordinate equations)
Three equations in two unknowns means that for the equations to be meaningful
there must be at least one pair of equations that are independent, and the
remaining equation must be a combination of these two . Two lines are parallel if
one directional vector is a scalar multiple of the other. So we take two independ-
ent equations, find the values of J,J. and "A (we have two equations in two un-
knowns), and put them in the third equation to see if they are consistent.
Example 7.3 , below, demonstrates this method, and listing 7.2 is a way of
implementing it on a computer. The first line has base and direction stored in
arrays Band D, and the second line in C and E: the calculated point of inter-
section goes into array P.
Note that if the two independent equations are
Three-dimensional Coordinate Geometry 131
then the determinant of this pair of equations .t. = a 11 x a22 - a 12 x a2l> will be
non-zero (because the equations are not related), and we have the solutions
Listing 7.2
Example 7.3
Find the point of intersection (if any) of
1+21J=0 -X (7.4)
1+ IJ=O+X (7.5)
1 + 31J = 1 + X (7.6)
From equations 7.4 and 7.5 we get IJ= - 2/3 and X= 1/3 , which when substituted
in equation 7.6 gives 1 + 3 x (-2/3) = -Ion the left-hand side and 1 + 1 x (1/3)
=4/3 on the right-hand side, which are obviously unequal , so the lines do not
intersect. From (b) we get the equations
2 + IJ = - 2 + X (7.7)
3 + IJ = -3 + 2X (7.8)
4 + IJ= - 4 + 3X (7.9)
and from equations 7.7 and 7.8 we get IJ = - 2 and X= 2, and these values also
satisfy equation 7.9 (left-hand side = right-hand side = 2). So the point of
intersection is
In order to solve this problem we must introduct a new vector operator, X the
vector product, which operates on two vectors p and q (say) giving the vector
result
Listing 7.3
10~ REM dot product and vector product
110 LET vecprod =
3~~: LET dotprod 40~ =
120 DIM L(3): DIM M(3): DIM N(3)
130 INPUT "VECTOR L","(";U1);",";L<Z);",";U3);")"
140 PRINT AT 1,0;"VECTOR L","(";U1);",";U2);",";L<3);")"
150 INPUT "VECTOR M","(";M(1);",";M(Z);",";M(3);")"
160 PRINT AT 4,0;"VECTOR M","(";M(1);",";M(2);",";M(3);")"
170 GO SUB vecprod
180 PRINT AT 8,1lI;"VECTOR PRODUCT","(";N(1);",";N(2);",";N(3);")"
190 GO SUB dot prod
20~ PRINT AT 11,0;"DOT PRODUCT",DOT
210 STOP
Listing 7.4
30~ REM vecprod
301 REM IN : L(3),M(3)
302 REM OUT : N(3)
309 REM N is the vector product of Land M.
310 LET NI = 2: LET NNI 3=
320 FOR I = 1 TO 3
330 LET N(I) =L(NI)*M(NNI)-L(NNI)*M(NI)
340 LET NI = NNI: LET NNI =
IH + 1 : IF NNI = 4 THEN LET NNI =1
350 NEXT I
360 RETURN
Suppose we are given three non-collinear points P I, P2 and P3' Then the two
vectors P2 - PI and P 3 - P 1 represent the directions of two lines coincident at
PI, both of which lie in the plane containing the three points. We know that the
normal to the plane is perpendicular to every line in the plane, in particular the
two lines mentioned above. Also, because the points are not collinear P2 - PI =1=
P3 - PI, the normal to the plane is (P2 - PI) X (P3 - P1), and since PI lies in
the plane the equation is
Example 7.4
Give the coordinate equation of the plane through the points (0, 1, 1), (1,2,3)
and (-2, 3, -1).
This is given by the general point x == (x, y, z) where
that is
or
We assume that the three planes are defined by equations 7.10 to 7.12 below.
The point of intersection of these three planes, x == (x, y, z), must lie in all
three planes and satisfy
(7.10)
n2 • x =k2 (7.11)
n3 • x =k 3 (7.12)
where nl == (nIb n12, nI3),n2 == (n2l> n22, n23) and n3 == (n31, n32, n33)' We
can rewrite these three equations as one matrix equation
Listing 7.5
50VJ REI': inv/er se of 3x3 matrix
501 IN : N<3,3)
502 OUT: SINGULAR, M(3,3)
510 LET SINGULAR = 1
520 LET DET = 0: LET NI = 2: LET NNI = 3
530 FOR I = 1 TO 3
540 LET DET = DET + N(1,I)*(N(2,NI)*N(3,NNI)-N(2,NNI)*N(3,NI»
550 LET NI = NNI: LET NNI = NI + 1 : IF NNI = 4 THEN LET NNI = 1
560 NEXT I
569 REM DETerminant of singular matr ix is zero, th ere is no inverse .
570 IF ABS DET < 0.000001 THEN RETURN
579 REM calculate M, the inverse of N, by the Adjoint method.
580 LET NI = 2: LET NNI = 3
590 FOR I = 1 TO 3
600 LET NJ = 2: LET NNJ = 3
610 FOR J = 1 TO 3
620 LET M(J,n = (N(NI,NJ)*N(NNI,NNJ) - N(NI,NNJ)*N(NNI,NJ»/DET
630 LET NJ = NNJ: LET NNJ = NJ + 1: IF NNJ = 4 THEN LET NNJ = 1
640 NEXT J
650 LET NI = NNI: LET NNI = NI + 1: IF NNI = 4 THEN LET NNI = 1
6611l NEXT I
6711l LET SINGULAR =
0
680 RETURN
the planes are parallel or the three meet in a line, then there is no unique point
of intersection : in these cases DET, the determinant of the matrix N is zero and
variable SINGULAR = I . (See listing 7.6.)
Example 7.5
Find the point of intersection of the three planes (0, I , I) " x = 2, (1 ,2,3) " x =
4and(l , I, 1)"x=O.
In the matrix form we have
The inverse of G ; ;) is ( -; _~ ~)
I 1 -1 1-1
and so
G) = C~ -: -1) X mCD =
This solution is easily checked : (0, 1,1) " (-2, 0, 2) = 2, (1,2,3) " (-2, 0, 2)
= 4 and (1 , 1, I) " (-2, 0 , 2) = 0 , which means the point (-2, 0, 2) lies on all
three planes and so is their point of intersection.
Weassume that the planes are not parallel, and so p =:/= 'Aq for all 'A. The line
common to the two planes naturally lies in each plane, and so it must be per-
pendicular to the normals of both planes (p and q). Thus the direction of this
line must be d == p X q and the line can be written in the form b + IJd, where b
can be any point on the line. In order to classify completely the line, we have to
find one such b. We find a point that is the intersection of the two planes,
together with a third that is neither parallel to them nor cuts them in a common
line. Choosing a plane with normal p X q will satisfy these conditions (and
Three-dimensional Coordinate Geometry 137
remember that we have already calculated this vector product). We still need a
value for k 3 , but any will do, so we take k 3 = 0 in order that this third plane
goes through the origin . Thus b is given by the column vector
Example 7.6
Find the line common to the planes (0, 1, 1) • x = 2 and (1,2,3) • x = 2. Since
p = (0 ,1 , 1) and q = (1,2,3), then p X q = (1 x 3 - 1 X 2 , 1 x 1 - 0 x 3,0 x
2 - 1 x 1) = (1, 1, -1). We require the inverse of
0 1
I 2
( 1 1
The program to solve this problem (listing 7.7) is given below; note it is very
similar to the previous program. Also note that arrays are not explicitly used for
p and q, these values are stored in the rust two rows of array N. Array B holds
the base vector of the line of intersection, but we do not place d in an array
because the values are already in the third row of N.
Listing 7.7
100 REM Line of intersection of two pLanes
110 DIM N(3,3 ): DIM M(3,3): DIM K(3) : DIM B(3): DI~ A$(2)
120 LET inv = 50rJ: LET AS = "PQ"
129 REM i nput data on two pLanes.
130 PR INT AT 2, 10i "COEFFICI ENTS CONSTANT"
140 FOR I = 1 TO 2
150 PRINT AT 2 + 2*I,lIli"PLANE("iIi")= ( , , )"
160 FOR J = 1 TO 3
1711l LET IS = "INPUT " + ASO) + "(" + STRS J + " ) "
180 INPUT (IS)iN(I,J): PRINT AT 2 + 2*1,7 + 4*JiN(I,J)
190 NEXT J
200 LET IS = "INPUT K(" + STRS I + ") "
210 INPUT (IS) ;K(I): PRINT AT 2 + 2*I ,28iK(I)
220 NEXT I
229 REM form t~i rd pLane.
230 LET N(3,1) = N(1,2)*N(2,3) - N(1,3)*N(2,2)
240 LET N(3,2) = N(1,3)*N(2,1) - N(1,1)*N(2,3)
250 LET N(3,3) = N(1,1)*N(2,2) - N(1,2)*N(2,1)
260 LET K(3) = III
269 REM if matrix of normaLs is singuLar then no intersection.
270 GO SUB i nv
280 PRINT AT 10, 3i "LINE OF INTERSECTION"
2911l IF S :~ElLAR THEN PRINT AT 10,0i"NO": STOP
311J0 PR!ln AT 12,2i"BASE VECTOR DIRECTION"
308 REM Line of intersect icn : -
309 REM base (B(1) , B( 2) , B(3» and direction (N(3,1),N(3,2),N(3,3».
310 FOR I = 1 TO 3
320 LET 80) = e
330 FOR J = 1 TO 3
340 LET B(I) = B(I) + M(I,J)*K(J)
350 NEXT J
360 IF ABS B(I) < 0.1Il01'!0rJ1 THEN LET eO) = 0
370 PRINT AT 12 + 2*I,lIli "B{ "iii") = " i B( I)
380 PRINT AT 12 + 2*1,20;"D("ili") = "iN(3,1)
390 NE XT I
40rJ STOP
n • x - k = n! x x + n2 x Y + n3 x z - k =0
This can be rewritten in functional form for a general point x == (x, y, z) on the
curve
[(x) == [(x, y , z) == n! x x + n2 x Y + n 3 x z - k == n • x - k
This is a simple expression in variables x, y and z (x) that enables us to divide all
the points in space into three sets, those with [(x) = 0 (the zero set) , with
[(x) < 0 (the negative set) and [(x) > 0 (the positive set). A point x lies on the
surface if and only if it belongs to the zero set. If the surface divides space into
two halves (each half being connected; that is, any two points in a given half can
Three-dimensional Coordinate Geometry 139
be joined by a curve that does not cross the surface) , then these two halves may
be identified with the positive and negative sets. Again beware, there are many
curves that divide space into more than two connected areas and then it is
impossible to relate functional representation with connected sets; for example,
[(x, y, z) = cos (y) - sin (x 2 + Z2). There are, however, many useful well-behaved
curves with this property, the sphere of radius r for example
that is,
[(x,y,z)=r 2 _x2 _ y 2 _ Z2
If [(x) = 0 then x lies on the sphere, if [(x) < 0 then x lies outside the sphere,
°
and if [(x) > then x lies inside the sphere.
The functional representation of a surface is a very useful concept. It can be
used to define sets of equations necessary in calculating the intersections of
various objects. The major use, however, is to determine whether or not two
points P and q (say) lie on the same side of a surface that divides space in two.
All we need do is compare the signs of [(P) and [(q). If they are of opposite
signs then a line joiningp and q must cut the surface. An example is given below.
Suppose the plane is defined (as earlier) by three non-collinear points Pi, P2 and
P3' Then the equation of the plane is
So all we need do for a point e (say) is to compare [(e) with [(0) , where 0 is
the origin. We assume here that neither 0 nor e lie in the plane .
We shall see that this idea is of great use in the study of hidden line algorithms.
Example 7.7
Are the origin and point (1, 1, 3) on the same side of the plane defined by points
(0,1 ,1), (1, 2,3) and (-2,3, -I)?
From example 7.4 we see that the functional representation of the plane is
Thus
and
Hence (1, 1,3) lies on the opposite side of the plane to the origin and so a line
segment joining the two points will cut the plane at a point (1 -/l) (0, 0, 0) +
/l(1, 1,3) where 0 <u < 1.
We start by assuming that the polygon is a triangle defined by the three vertices
Pi == (Xl ' Yl),PZ == (Xz. Yz) and p , == (X3 . Y3 )' Although these points are in
two-dimensional space we can assume they lie in the xjy plane through the
origin of three-dimensional space by giving them all a z-coordinate value of zero.
We systematically define the directions of the edges of the polygon to be
(Pz - pd, (P3 - pz) and (Pi - P3)' Since these lines all lie in the x/y plane
through the origin we know that for all i = 1, 2 or 3 and for some real numbers
ri that depend on i
This is because this vector product is perpendicular to the xly plane and so only
z-coordinate values can be non-zero. The addition of subscripts is modulo 3.
Because the vertices were taken systematically, note that the signs of these ri
values are always the same - but what is more important, if the Pi are clockwise
then the rl are all negative, and if the PI are anti-clockwise the rj are all positive.
Given an oriented convex polygon, we need consider only the first three
vertices to find if it is clockwise or anti-clockwise . This technique will prove to
be invaluable when we deal with hidden line and surface algorithms later in this
book. Listing 7.8 allows us to find whether or not three ordered two-dimensional
vertices form an anti-clockwise triangle.
Example 7.8
Why is the polygon given in example 3.4 anti-clockwise?
The vertices (considered in three dimensions) are (1 ,0,0), (5,2,0), (4 , 4, 0)
and (-2 ,1 ,0). The directions of the edges are (4, 2, 0) , (-1,2,0), (-6, -3,0)
and (3, -1,0). Then, since
Three-dimensional Coordinate Geometry 141
Listing 7.8
10~ REM orientation of 2-D tr iangle
110 DI~ X(3): DI~ Y(3)
120 LET H = "TYPE IN COORDINATES OF TRIAtlGLE "
13~ FOR I = 1 TO 3
140 LET IS = "VERTEX(" + STR$ I + ")= ("
150 INPUT (J$ + IS)iX(I)i","iYCI)i") "
16~ P RINT AT 2 + 2*I,0il$iXCI)i","iY(I)i")"
1i'~ NEXT I
180 PRINT AT 12,0i"THE TRIANGLE I~ "t
188 REM (DX1,DY1) is 2-D direction vector jcining point 1 to point 2 .
189 REM CDX2,DY2) ; !, (-0 direction ve ctor j oi rrinq point 2 to point 3.
1ge LET DX1 =X(2) - X(1): LET DY1 =Y(2) - Y(1)
20~ LET DX2 = X(3) - X(2): LET DY2 = Y(3) - Y(Z)
209 REM use 3 -D vector product to check on orientation of triangle.
2Hl IF DX1*DY2 - DX2*DY1 > ~ THEN PRINT "ANTI-"i
220 PR INT "CLOCKWISE": STOP
are all positive, so the orientation of the polygon is anti-clockwise. But be care-
fu1, if you lose this consistent order for calculatingthe vector product you can
get the wrong answer. For example
Complete Programs
I. Listing 7.1 (intersection of line and plane). Data required: a base vector
(B(l), B(2), B(3)) and direction vector (D(1), D(2), D(3)) for the line, a
normal (N(1), N(2), N(3)) and constant K for the plane. Try (1,2,3),
(1,1, -1), (1,0 ,1) and 2 respectively.
II. Listing 7.2 (intersection of two lines). Data required: a base and direction
vectors for the two lines. (B(1), B(2), B(3)) and (D(1), D(2) , D(3)), and
(C(1) , C(2) , C(3)) and (E(1), E(2), E(3)). Try (1,2,3), (1, 1, -1), and
(-1,1,3), (1, 0,1).
III. Listings 7.3 and 7.4 (main program, 'vecprod' and 'dotprod'). Data required:
two vectors (L(1), L(2), L(3)) and (M(l), M(2), M(3)). Try (1, 2, 3),
(1,1, -1).
142 Advanced Graphics with the SinclairZX Spectrum
IV. Listings 7.5 ('inv') and 7.6 (intersection of three planes). Data required:
normal (N(I, 1), N(I, 2), N(I, 3» and constant K(I) for the three planes,
1 ~I ~3 . Try (1, 2, 3), 0,(1 ,1, -1),1,(1,0 ,1),2.
V. Listings 7.5 ('inv') and 7.7 (intersection of two planes). Data required :
normal (N(I, 1), N(I, 2), N(I, 3» and constant K(I) for the two planes,
1 ~1~2. Try(I,2,3),0,(l, 1,-1), 1.
IV. Listing 7.8 (orientation of2-D triangle). Data required: the vertices
(X(I), YO» , 1 ~ I ~ 3. Try (1,2), (2, 3) and (-1 , 1).
8 Matrix Representation of
Transformations on Three-
dimensional Space
x' =A ll X X +A 12 X Y +A 1 3 X Z +A 14
y' =A 2 1 X X +A 2 2 X Y +A 2 3 X Z +A 24
Z' =A 3 1 X X +A 32 X Y +A 3 3 X Z +A 3 4
Listing 8.1
9100 REM roul t3
91r1 REM IN : AC4,4),RC4,4)
9102 REM OUT : RC4,4)
9110 FOR I = 1 TO 4
9120 FOR J = 1 TO 4
9130 LET AF = 0
91411 FOR K = 1 TO 4
9150 LET AR = AR + ACI,K)*RCK, J)
9160 NEXT K
9170 LET BCI,J) = AR
9180 NEXT J
91 <)g NEXT I
9200 FOR I = 1 TO 4
9210 FOR J = 1 TO 4
9220 LET RCI,J) = BCI,J)
9230 NEXT J
92411 NEXT I
9250 RETURN
9300 REM idR3
9302 RE~ OUT : RC4,4)
9310 FOR I = 1 TO 4
9320 FOR J = 1 TO 4
9330 LET R(I,J) = 0
93411 NEXT J
9350 LET R(I,I) = 1
9360 NEXT I
9370 RETURN
Translation
X' = 1 x x + 0 x y + 0 x z + TX
y' =0 x x + 1 x y + 0 x z + TY
z' =0 x x + 0 x y + 1 x z + TZ
o1
0 0
1 0
TX)
TY
( 0 o
1 TZ
000 1
The routine 'tran3' for producing such a matrix A , given the parameters TX, TY
and TZ is given in listing 8.2.
Listing 8.2
90rJrl REM tran3
90rJ1 REM IN : TX, TV, TZ
90rl2 REM OUT: A(4,4)
90HI FOR I = 1 TO 4
9020 FOR J =
1 TO 4
9030 LET A(I,J) 0 =
90t,g NEXT J
9050 LET AU,1) 1 =
9060 NEXT I
9070 LET A(1,4) =
TX: LET A(2,4) = TY: LET A(3,4) = TZ
9080 RETURN
Scaling
x' = SX x x + 0 x y + 0 x z + 0
y' = 0 x x + SY x y + 0 x z + 0
Z' = 0 X X + 0 x y + SZ x z + 0
o
SY
o
o
146 Advanced Graphics with the SinclairZX Spectrum
Usually the scalingvalues are positive, but if any of the values are negative then
this leads to a reflection as well as (possibly) scaling. For example, if SX =-1
and SY =SZ = 1 then points are reflected in the y/z plane through the origin. A
routine 'scale3' to produce such a scaling matrix A given SX, SY and SZ is given
in listing 8.3.
Listing 8.3
8901' REM scaLe3
8901 REM IN : SX,SY,SZ
8902 REM OUT : A(4,4)
8910 FOR I = 1 TO 4
8920 FOR J = 1 TO 4
8930 LET A(I,J) = 0
8940 NEXT J
8950 NEXT I
8960 LET A(1,1) = SX: LET A(2,2) = SY: LET A(3,3) = SZ
8970 LET A(4,4) = 1
8980 RETURN
In order to consider the rotation about a general axis p + IJq by a given angle, it
is first necessary to simplify the problem by considering rotation about one of
the coordinate axes.
\lLx
x z
x z
z y
z-axis into page y-axis into page x-axis into page
Figure 8.1
Referring to figure 8.1c, the axis of rotation is perpendicular to the page (the
positive x-axis being into the page), and since we are using left-handed axes the
figure shows the point (x' , y', a') resulting from the transformation of an arbitrary
point (x, y, z). We see that the rotation actually reduces to a two-dimensional
rotation in they/z plane passing through the point; that is, after the rotation the
x-coordinate remains unchanged. Using the ideas explained in chapter 4 we have
the following equations
Matrix Representation of Transformations on Three-dimensional Space 147
x'=x
y' = cos (J x y - sin (J x z
z' = sin (J x y + cos (J x z
o o
D
cos (J -sin (J
sin (J cos (J
o o
Referring to figure 8.1 b, we now have the positive j-axis into the page and,
because of the left-handedness of the axes, the positive z-axis is horizontal and
to the right of the origin while the positive x-axis is above the origin. This leads
us to the equations
COS (J 0 sin (J
o 1 o
(
- sin (J 0 cos (J
o 0 o
COS (J -sin (J
sin (J cos (J
(
o o
o o
148 Advanced Graphics with the Sinclair ZX Spectrum
A subprogram 'rot3' to produce such a matrix A, given the angle THETA and
the axis number AXIS (AXIS = I for the x-axis, AXIS =2 for the y-axis and
AXIS = 3 for the z-axis) is given in listing 8.4 .
Listing 8.4
Inverse Transformations
(l) A translation by (TX, TY, TZ) is inverted with a translation by (- TX, - TY,
-TZ);
(2) a scaling by SX, SY and SZ is inverted with a scaling by l/SX, l/SY and
l/SZ ;
(3) a rotation by an angle 8 about a given axis is inverted with a rotation by an
angle -8 about the same axis;
(4) if the transformation matrix is the product of a number of translation,
scaling and rotation matrices A X B X ex ... X L X M X N, then the inverse
transformation is
Matrix Representation of Transformations on Three-dimensional Space 149
Assume p == (PX, PY, PZ) and q == (QX, QY, QZ) . We break down the task into a
number of sub tasks
(a) We translate all of space so that the axis of rotation goes through the origin .
This is achieved by adding a vector -p to every point in space with a matrix F
say , which is generated by a call to 'tran3' with parameters TX = -PX, TY = -PY
and TZ = -PZ . The inverse matrix F- l will be needed later and is found by a call
to 'tran3' with parameters PX , PY and PZ. After this transformation the axis of
rotation is the line 0 + uq passing through the origin .
F=
1 0 0 -PX)
0 1 0 -PY
( o 0 1 -PZ
rl = I 00
(1 0 0 PX)
1 0 PY
0 1 PZ
0001 1
0 0 0 1
(b) We then rotate space about the z-axis by an angle -0:, where (ALPHA =) 0: =
tan"! (QY/QX), given by the matrix G. The matrix is generated by a call to
'rot3', setting THETA = -ALPHA and AXIS = 3, and the inverse matrix G- l by
a call to 'roB ' with THETA = ALPHA and AXIS =3 . At this stage the axis of
rotation is a line lying in the xlz plane passing through the point (v, 0, QZ) .
QX QY 0 0) (QX -QY 0 0)
G =~ -QY QX 0 0 c:' = ~ QY QX 0 0
v
( 0 0 vO v 0 0 vO
o OOv OOOv
QZ 0 -v 0)
H- l O w 0 0 H- l _
(QZ 0 v
lOw a 0
0)
w ( v 0 QZ 0 w -v a QZ a
0001 0001
where w is the positive number given by w2 =v2 + QZ2 =QX 2 + Qy2 + QZ2 .
SO the point (v , 0, QZ) is transformed to (0, 0 , w) , hence the axis of rotation is
along the a-axis .
(d) We can now rotate space by an angle 'Y (GAMMA) about the axis of rotation
using matrix W generated by 'roB ' (with AXIS = 3 and THETA = GAMMA).
I)
150 Advanced Graphics with the Sinclair ZX Spectrum
COS 'Y
W = sin 'Y
-sin 'Y
cos 'Y
°° I
o ° °
° °
( I
o I
(e) We need to return the axis of rotation to its original position so we multiply
B- 1 , c:' and finally F- l •
Thus the final matrix P that rotates space by the angle 'Y about the axis
p + uq is P =F- l X G- l X B- 1 X W X H X G X F. Naturally some of these
matrices may reduce to the identity matrix in some special cases, and can be
ignored . For example, if the axis of rotation goes through the origin then F and
p-l are identical to the identity matrix I, and can be ignored.
So it is possible to write a special routine 'genrot' (listing 8.5) that achieves
this rotation and returns the required matrix P given GAMMA, (PX, PY, PZ) and
(QX, QY, QZ).
Listing 8.5
5800 REM genrot
5801 REM IN : PX,PY,PZ,QX,QY,QZ,GAMMA,R(4,4)
5802 REM OUT : R(4,4)
5809 REM pla ce origin on a xis of rotation.
5810 LET TX = -PX: LET TY = -PY: LET TZ = -PZ: GO SUB tran3: GO SUB mult3
5819 REM rotate axis of rotation into x / z plane.
5820 LET AX = QX: LET AY = QY: GO SUB angle
5830 LET ALPHA = THETA: LET THETA = -THETA: LET AXIS = 3: GO SUB rot3
: GO SUB mul t3
5839 REM rotate axis of rotat ion onto z-axis.
584la LET AX = QZ: LET AY = SQR (QX*QX + QY*GY): GO SUB angle
5850 LET BETA = THETA: LET THETA = -THETA: LET AXIS = 2: GO SUB rot3
: GO SUB mul t3
5859 REM rotate by GAMMA about axis of rotation.
5860 LET AXIS = 3: LET THETA = GAMMA: GO SUB rot3: GO SUB mult3
5869 REM replace axis back to or iginal position.
5870 LET AXIS = 2: LET THETA = BETA: GO SUB rot3: GO SUB mult3
5880 LET AXIS = 3: LET THETA = ALPHA: GO SUB rot3: GO SUB mult3
5890 LET TX = PX: LET TY = PY: LET TZ = PZ: GO SUB tran3: GO SUB mul t3
5900 RETURN
EXQmple8.1
What happens to the points (0, 0 , 0), (1,0,0), (0, 1,0), (0, 0, I) and (1, I, 1) if
space is rotated by rr/4 radians about an axis (1,0, 1) + fJ.(3, 4, 5) .
Using the above theory we note that
F=
I °°° -1)
(oo °° ° °
0 I
1 -I
1
Matrix Representation of Transformations on Three-dimensional Space 151
G=!eaa aa a a
5
400)
3a a
5
G- 1 =.!.
5
C o
a 0 5 a
0)
4 -43 a a
5 a 0 a 5
~l 0
1 0../2 a
-1
n- 1 = _1 ( 0a 0)
0../2 1 0
H = -"';2 1 a 1
o a a vV ../2 -1 a 1
o a a
0
../2
~ j)
-1 a
1 a
and
W="/2 a a ../2
Cl
a a
-12 - 13y'2 -15 + 35-/2 -26 + 6v'2)
1 -12 + 9-/2 34 + 16../2 -20 + 5../2 32 - 42../2
+ 37../2
P» - -
50../2 -15 - 5../2 -20 + 35../2 25 + 25../2 -10 + 30../2
a o a 50../2
and the angle GAMMA, and rotates any point (XX, YY, ZZ) about this axis by
angle GAMMA is given in listing 8.6.
Listing 8.6
100 REM rotation of a point about a given axis
110 DIM A(4,4): DIM BC4,4): DIM RC4,4)
120 DIM P(3): DIM AS(8)
130 INPUT ''BASE VECTOR OF AXIS ","C";PX;",";PY;",";PZ;")"
14l' PRINT AT 1,0; "BASE VECTOR OF AXIS ", "C";PX;",";PY;",";PZ;")"
150 INPUT "DIRECTION VECTOR OF AXIS ","C";QX;",";QY;",";QZ;")"
160 PRINT AT 4,0; "DIRECTION VECTOR OF AXIS ","C";QX;",";QY;",";QZ; ")"
170 INPUT "ANGLE OF ROTATION ";GAMMA
180 PRINT AT 7 ,0;"ANGLE OF ROTATION " ; G A~l MA
190 LET R1Llt3 = 9100: LET idR3 = 9300: LET rct3 = 8600
: LET tran3 = =
9000: LET angle =
8800: LET genrot 5800
198 REM calculate RC4,4) for rotating point by angle GAMMA about an axis
199 REM with base vector CPX,PY,PZ) and di rection vector CQX,QY,QZ).
200 GO SUB idR3: GO SUB genrot
210 PRINT AT 14,0;"BECOMES"
219 REM input and transform point CXX,YY,ZZ).
220 INPUT "POINT VECTOR","C";XX;",";YY;",";ZZ;")"
230 PRINT AT 12,0;"POINT C";XX;",";YY;",";ZZ; ")""
24l' LET P(1) :, XX*R(1,1) + YY*R(1,2) + ZZ*RC1,3) + RC1,4)
250 LET P(2) = XX*RC2,1) + YY*RC2,2) + ZZ*R(2,3) + R(2,4)
260 LET P(3) = XX*RC3,1) + YY*RC3,2) + ZZ*RC3,3) + RC3,4)
269 REM tidy up o utput.
270 PRINT AT 16, 0 ; " C" ;
280 FOR I= =
1 TO 3: LET AS =
STRS PCI): FOR J 1 TO 8
290 IF ASCJ) <> " " THEN PRINT ASCJ);
300 NEXT J: IF I <> 3 THEN PRINT ", ";
310 NEXT I: PRINT ")""
320 GO TO 220
Exercise 8.1
Experiment with these ideas. You can always make a check on your final trans-
formation matrix by considering simple values as above , and you can use the
previous listings to check your answer. It is essential that you are confident in
the use of matrices, and the best way to get this confidence is to experiment. You
will make lots of arithmetic errors initially, but you will soon come to think of
transformations in terms of their matrix representation, and this will greatly ease
the study of drawing three-dimensional objects.
Exercise 8.2
As with the two-dimensional case, we note that the 'bottom row' of all trans-
formation matrices is always (0, 0, 0, 1), and is of no real use in calculations. It
is added only to form square matrices, which are necessary for the formal
definition of matrix multiplication. We can adjust this definition , and that of the
multiplication of a matrix and a column vector, so that instead we use only the
top three rows of the 4 X 4 matrices (in chapter 4 we used the top two rows of
3 X 3 matrices in listings 4.2a, 4.3a, 4.4a and 4.5a). Change listings 8.1,8 .2,8.3
and 8.4 accordingly.
Matrix Representation of Transformations on Three-dimensional Space 153
Exercise 8.3
You will have noticed that the routine 'rot3' is usually called with THETA
generated by 'angle', which uses values AX and AY as input parameters. 'rot3'
calculates the cosine and sine of angle THETA, but we know that these are
AX/y(AX 2 + Ay 2 ) and AY/y(AX 2 + Ay 2 ) respectively. Write another
rotation routine 'rotxy' that calculates the rotation matrix directly from AX
and AY without resorting to 'angle'.
Also we note that the first stage of the 'rot', 'tran', 'scale' and 'idR' routines
consists of clearing an array . This can be achieved more efficiently on the
Spectrum by reDIMensioning the array. Furthermore it is often faster to ex-
plicitly assign values rather than calculate them inside FOR...NEXT loops.
Exercise 8.4
Again in chapter 4 we noted that some writers use row rather than column
vectors, and postmultiply rather than premultiply . We decided against this inter-
pretation, so that the matrix of a transformation corresponds directly with the
coefficients of the transformation equations . In this other interpretation it is the
transpose of the matrix that is identical to the coefficients. It is useful to be
aware of this other method, so use it to rewrite all the programs given in this
chapter (and the remainder of this book) . Remember though, it is not important
which method you finally decide to use, as long as you are consistent. We use
the column vector notation because we have found that it causes less confusion
in the early stages of learning the subject!
Compie Programs
I. All the listings in this chapter, 8.1 ('mult3' and 'idR3'), 8.2 ('tran3'), 8.3
('scale3'), 8.4 ('rot3'), 8.5 ('genrot'), 8.6 (main program) and listing 3.4
('angle'). Required data: base vector (PX, PY, PZ), direction vector (QX, QY,
QZ) of the axis of rotation and the angle GAMMA, together with any number
of three-dimensional coordinates (XX, YY,ZZ). Try (0, 0, 0),(1,1,1) and
rr/4, and points (1,0, 1), (1, 1, 1), (1,2,3).
9 Orthographic Projections
Most scenes will be composed of simple objects (for example , cube(s), see ex-
ample 9.1) that are set at a peculiar position and orientation in space. It is very
inefficient to calculate by hand the complicated coordinates of every vertex of
these objects and input them into the program. Instead we look at each object in
turn and initially defme it in an elementary way relative to the ABSOLUTE triad ,
usually setting it about the origin. The information required will be that of
vertices (x-coordinate,y-coordinate and z-coordinate), and perhaps lines (that
join pairs of vertices) or (later when we consider hidden line and hidden surface
algorithms) facets, which are polygonal planar areas bounded by the above-
mentioned lines. This elementary definition of the object is called its SETUP
position. We could have other information also, such as the colour of the object.
We use the matrix techniques of the last chapter to generate a matrix that will
move the object from its SETUP position to its required ACTUAL position
relative to the ABSOLUTE axes. We shall call this SETUP to ACTUAL matrixP.
Orthographic Projections 155
Nothing could be simpler. In the orthographic projection we can set the view
plane to be any plane with normal vector along the line of sight. When trans-
formed into the OBSERVED position, the view plane will be any plane parallel
to the xly plane given by the equation z = O. For simplicity we take the x/y
plane through the origin. The vertices of the object are projected on to the view
plane by the simple expedient of setting their z-coordinates to zero . Thus any
two different points in the OBSERVED position, (x, y, z) and (x, y, z') say
(where z =1= z'), are projected on to the same point (x,y, 0) on the view plane .
Then we identify the xfy values on the plane with points in the graphics screen
coordinate system (usually centred on the screen) using the methods of chapter
2 . Once the vertices have been projected on to the view plane and then on to the
screen, we can construct the projection of lines and facets. These are related to
the projected vertices in exactly the same way as the original lines and facets are
related to the original vertices.
156 Advanced Graphics with the Sinclair ZX Spectrum
Before considering in detail the general case where the eye and direction of
view are arbitrarily positioned, we take an elementary example to demonstrate
the orthographic projection.
Example 9.1
Use the above ideas to draw an orthographic projection of a cube . Figures such
as those in figure 9.1 are called wire diagrams or skeletons (for obvious reasons) .
In the SETUP position the cube may be thought to consist of eight vertices
(1,1,1),(1,1 ,-1),(1 ,-1,-1),(1,-1,1),(-1,1 , 1),(-1, 1,-1),(-1 ,-1,-1)
and (-1, -1, 1): vertices labelled numerically 1 to 8. The twelve lines that form
the wire cube join vertices 1 to 2,2 to 3,3 to 4 , 4 to 1; 5 to 6,6 to 7, 7 to 8,
8 to 1; 1 to 5, 2 to 6, 3 to 7,4 to 8.
Figure 9.1 a shows the simplest possible example of an orthographic projec-
tion of the cube, where even the SETUP to ACTUAL matrix is the identity
matrix ; that is, the cube stays in its SETUP position. We get a square: pairs of
parallel lines from the front and back of the cube project into the same line on
the screen. We put a "+" in these diagrams to show the position of the z-axis in
the OBSERVED position (into the screen).
Figure 9.1b shows the same cube drawn after the following three transforma-
tions place it in its ACTUAL position .
(a) Rotate the cube by an angle a: = -0.927295218 radian about the z-axis -
matrix A . This example is contrived so that cos a: = 3/5 and sin a: = -4/5 ,
ensuring that the rotation matrices consist of uncomplicated elements.
(b) Translate by the vector (-1 , 0, 0) - matrixB.
(c) Rotate by an angle -a about the y -axis - matrix C.
0) (0o -1)oo
C C5
5 4/5 0 0 4/5
o
-4~5 3/5 o 0
D
B= 0 1 0 C= 0 1 0
A=
0 1 0 0 1 -4/5 0 3/5
0 o 1 o 0 0 1 0 0 0
and P is given by
p=~eo
12 20 -15)
15 0 0
25 -12 -16 15 20
0 o 0 25
So the above eight vertex coordinate triples in the SETUP position are trans-
formed into the following eight ACTUAL coordinate triples : (26/25, -5/25,
7/25), (-14/25, -5/25, -23/25), (-38/25, -35/25 , 9/25), (2/25, -35/25 ,
Orthographic Projections 157
-1 ~9
-20 12 20
15 0 -15)
0 X (1
1 ) = - 1 (26)
-5
25 -12 -16 15 20 1 25 7
o 0 0 25 1 25
Since the ACTUAL to OBSERVED matrix Q is the identity matrix, the pro-
jected coordinates on the view plane are thus (26/25, -5/25), (-14/25, -5/25),
(-38/25 , - 35/25), (2/25, -35/25), (8/25 , 35/25), (-32/25,35/25), (-56/25,
5/25), (-16/25,5/25). We can place these points on the screen and join them
with lines in the same order as they were defined in the SETUP cube .
. J\
...
..... \\. ..."'"
...•.. .....•
,.,f
.'
\ .../ '
l ,·I'··
+ ~-----'I:"i
"\
" +
\ •..l '/ '
"
'\ .
.,./, ,'"
\ ., ,.,0'/
... \ .'
\ 01.... ..:,\,,/ "
(a) (b)
(c) (d)
Figure 9.1
158 Advanced Graphics with the Sinclair ZX Spectrum
We assume that the eye is at (EX, EY, EZ) relative to the ABSOLUTE axes, look-
ing towards the point (DX, DY, DZ). The OBSERVED position is achieved in the
following sequence of steps .
(1) A matrix D translates all the points in space by a vector (-DX, -DY, -DZ)
so that now the eye is at (EX - DX, EY - DY, EZ - DZ) = (FX , FY, FZ) say,
looking towards the origin.
1 0 0 -DX)
D= 0 1 0 -DY
( o 0 1 -DZ
o0 0 1
(2) A matrix E changes (FX , FY, FZ) into (r, 0, FZ) by rotating space by an
angle -a where 0: = tan"! (FY/FX) about the z-axis, Here r 2 = FX 2 + Fy 2 and
r>O .
00)
~
FX FY
E= ~ -FY FX 0 0
rOO r 0
o 0 0 r
(3) A matrix Ftransforrns (r, 0, FZ) into (0,0, -s) by rotating space by an angle
rr - 8 about the y-axis , where 8 =tan -1 (r/FZ). Here S2 =r 2 + FZ 2 = FX 2 +
Fy2 + FZ 2 and s > O.
- FZ 0 r
F= ~ 0 s o
s ( -r 0 -FZ
o 0 o
(4) The transformation thus far places the eye at (0, 0, -s) on the negative z-
axis looking towards the origin and at the same distance from it (s) as (EX, EY,
EZ) was from (DX, DY, DZ). We now generate a matrix G, which moves the
eye to the origin.
G=
1 0
0 1
o
oo0
0 1 s
0)
(
o 0 o 1
H= ~(:
t 0
o
and thus
Listing 9.1
820~ REM look3
821~ INPUT "(EX,EY,EZ) "; EX; ", "; EY; ", "; EZ
8220 INPUT "(OX,OY,OZ) ";OX;",";OY;", ";OZ
8229 REM move origin to (OX,OY,OZ).
8230 LET TX = OX: LET TY = OY: LET TZ = OZ
8240 GO SUB tran3: GO SUB mult3
8249 REM move eye onto negative z-axis, looking at the or igin.
8250 LET FX = EX - OX: LET FY = EY - OY: LET FZ = EZ - OZ
8260 LET AX = FX: LET AY = FY: GO SUB angle
827~ LET AXIS = 3: LET THETA = -THETA: GO SUB rot3: GO SUB mul t3
8280 LET AX = FZ: LET AY = SQR (FX*FX + FY*FY): GO SUB ang le
8290 LET AXIS = 2: LET THETA = PI - THETA: GO SUB rot3: GO SUB mult3
8299 REM maintain vertica l.
830~ LET TX = 0: LET TY = 0: LET TZ = SQR (FX*FX + FY*FY + FZ*FZ)
831~ LET AX = TZ*FX: LET AY= -FY*FZ: GO SUB angle
8320 LET AXIS = 3: GO SUB rot3: GO SUB mult3
8329 REM move eye to the origin: space is now in the OBSERVED position.
833~ GO SUB tran3: GO SUB mul t3
8340 RETURN
If required, we can extend this program to deal with the situation where the
head is tilted through an angle r from the vertical. This is achieved by further
rotating space by -r about the z-axis, Thus matrix H should then rotate about
the z-axis by an angle ~ - r.
The construction of the ACTUAL to OBSERVED matrix is obviously inde-
pendent of everything other than the position of the eye, line of sight and the
tilt of the head. So if we wish to view a series of objects from the same position,
we can store Q and use it repeatedly for placing each object.
It is now time to deal with the problem of representing objects to the computer.
There is no definite solution, it really depends on what is being drawn and how
it is projected. In this section we describe various ways of setting up a data-base
to hold the information necessary for drawing any given scene, but make no
comment on their usefulness. This is considered in the remainder of the book
where we give examples to illustrate the value of particular methods in different
situations. Weshall be using arrays to hold large sets of data, and so naturally
the amount of space given to arrays will depend on the amount of information
required for a scene: be sure that when you declare these arrays there is enough
space for all the information; if in doubt, overestimate your store requirements.
Vertices. We will always need to define vertices and other special reference
points in a scene, and these we store as x, y and z-coordinates in arrays X, Y and
Z respectively, assuming that if the total number is not known explicitly, then
this value is calculated as NOV. So there must be space for not less than NOV
values in each of the three arrays. These vertices may be in the SETUP, ACTUAL
or OBSERVED position, it depends on the context of the problem. There will
Orthographic Projections 161
also be situations (perspective in particular) when we need to store the xjy -co-
ordinates of the projections of these NOV vertices - in arrays V and W. Naturally
this is unnecessary in the case of an orthographic projection of points in the
OBSERVED position since we can use the values already stored in the X and Y
arrays . The choice of data-base really depends on the scene and type of projection.
Lines. We can store information on NOL (say) line segments in the two dimen-
sional integer array L. The Ith line is defined by the integer indices (between I
and NOV) of the two points at each end of the line - we store the indices in
L(I, I) and L(2 , I) . The true coordinate values of the two points at each end of
the line segment can be found from the X, Y and Z arrays.
Facets. A facet is a convex polygonal area on the surface of a three-dimensional
object, and can be defined in a number of ways. Most facets will be triangular or
quadrilateral, so we usually assume that no facet has greater than six sides to
minimise waste of store . The NOF facets can be defined in terms of the indices
of the vertices at their corners in array F ; F(I, J) is the index of the Ith vertex on
the Jth facet. Naturally if the facet is not hexagonal then some of the values are
garbage so we need to store array H, the number of vertices/edges on each facet.
We can also store C, the integer colour code (if any) for each facet; but be care-
ful, the Spectrum allows only two colours in anyone character block. Another
method is to store the facet in terms of the indices of the lines in the object in
array F, which would thus refer to array L; F(I, J) would now be the index of
the Ith line on the edge of the J th facet . There are many other methods for
representing these, and other elements of a three-dimensional object : choose the
one most suitable to your particular situation.
For any required object we define a construction routine that needs, as para-
meters, a matrix R to move vertices into position and any other information
about the size of the object (if the object is to be stored in the SETUP position
then naturally no matrix is needed). The routine can then define the vertices,
lines, facets or any other elements of the object, and use the matrix R to move
the vertices of the object into the required position. Depending on the context
of the program, the routine can then either draw the object, or extend a data-
base containing this information. We shall give examples of both methods.
We can construct a scene containing a number of similar objects (so the data
will be in either the ACTUAL or the OBSERVED position) . There is no need to
produce a new construction routine for each occurrence of the object, all we do
each time is calculate a new SETUP to ACTUAL matrix P, and enter it (for the
ACTUAL position) or Q X P (for the OBSERVED position) into the same
routine. Naturally we require one new routine for each different type of object.
The complete scene is achieved by the execution of a main program (listing
9.2), which declares all the subroutine labels, then prepares the graphics screen
162 Advanced Graphics with the Sinclair ZX Spectrum
using input values of HORIZ and VERT and finally calls a routine 'scene3' that
organises the objects in space and then draws them. The main program below
will be used in all the three-dimensional graphics programs that follow, so do
not alter it without very good reason .
Listing 9.2
'scene3' declares all the arrays that are required for storing information about
a scene, together with matrices zl, B. R and (perhaps) Q for moving objects into
position. Any other subroutine labels unique to this scene are also declared. If
required the values of NOV and NOL (or NOF) are initialised, and these will be
updated in later construction routines. For each individual object (a 'block),
'scene3 ' must calculate a matrixP that moves this block into the ACTUAL
position , and then call the construction routine using the correct matrix R (per-
haps SETUP to ACTUAL or SETUP to OBSERVED). All the blocks finally
construct the fmished scene. Sometimes the drawing of the projection is done
inside the construction routine , or it can be elsewhere in other routines specifi-
cally designed for special forms of drawing (as in hidden line and hidden surface
pictures) : it depends on what is being drawn and what is required of the view. As
usual, because of the restriction of not passing array parameters into subroutines,
we do not normally explicitly generate P and Q, but rely instead on updating
matrixR . If we require the ACTUAL to OBSERVED matrix then this routine
calls '100k3'. Should we need to store Q then we must first call 'idR3', which
sets matrix R to the identity ; remember all matrix operations are done via
matrices A and R , using matrix B to hold intermediate values.
Always use the clipping version of 'lineto' in your programs. It is so easy to
choose values of HORIZ and VERT that are too small, with the result that part
of the object goes outside the graphics area . Without the 'clip ' routine the pro-
gram will fail. There is a positive reason, however , for making these values small :
it enables us to zoom in on a small area of a scene, drawing it very large, and all
the exterior lines will be clipped away.
Our first example of this method is listing 9.3, which is the 'scene3' routine
needed to construct a picture of a single cube as shown in figure 9.1 d. The scene
Orthographic Projections 163
can be viewed from any position with the vertical maintained. Wehave also a
construction routine 'cube' (listing 9.4) that generates the data for a cube with
sides oflength 2. It places the vertices, eight sets of coordinate triples, in arrays
X, Y and Z. There is no need to store the lines of the cube explicitly, we get the
information from a DATA statement and draw the lines straight away. The data
for figure 9.ld are HORIZ =6, VERT =4, (EX, EY, EZ) =(-2,2,2) and
(DX, DY, DZ) =(-1,0,0).
Listing 9.3
6000 REM scene3/e xampLe 9.1
6010 DIM X(8): DI~ Y(8): DIM z(e)
6020 DIM A(4,4): DIM B(4,4): DIM R(4,4)
6~ 3e LET cube = 6500
6039 REM caLcuLate the SETUP to ACTUAL matr ix R.
6040 GO SUB idR3
6~5~ LET THETA = -0.92729522: LET AXIS = 3: GO SUB rot3 : GO SUB muLt3
6060 LET TX = -1 : LET TY = 0: LET TZ = 0: GO SUB tran3: GO SUB muLt3
6070 LET THETA = -THETA: LET AXIS = 2: GO SUB rot3: GO SUB muLt3
6079 REM change R: premuLt ipLy i t by the ACTUAL to OBSfRVED matrix.
6080 GO SUB look3
6089 REM caLL construction rout irE draw the cube.
6090 GO SUB cube
6100 RETURN
Listing 9.4
6500 REM cube / Lines (not stored)
6501 REM IN R(3,3)
6510 DATA 1,1,1, 1,1,-1, 1,-1,-1, 1,-1,1, -1,1,1, -1,1,-1, -1,-1,-1, -1,-1,1
6520 DATP 1,2, 2,3, 3,4, 4,1, 5,6, 6,7,7,8, 8,5, 1,5 , 2,6, 3,7, 4,8
6530 RESTORE cube
6539 REr" ;r ,put SETUP vert ices of cube and mov e them i nt o OBSERVED POSITION.
6540 FOR I = 1 TO 8
6550 READ XX, YY, ZZ
6560 LET XCI) = XX*R (1,1) + YY*R(1,2 ) + ZZ*R(1,3) + R( 1,4)
6570 LET yell = XX*R(2, 1) + YY*R(2,2) + ZZ*R(2,3) + R(Z,4)
6580 LET Z(I) = XX*R(3,1) + YY*R(3,2 ) + ZZ*R(3,3) + R(3,4)
6590 NE XT I
6599 REr~ i nput Line i nf ormat i on : draw Lines by j oin ing pairs of vertices.
6600 FOR! = 1 TO 12
6610 READ L1,L2
6620 LET XPT = X(L1): LET YPT = Y(L1): GO SUB moveto
6630 LET XPT = X(L2) : LET YPT = Y(L2): GO SUB Lineto
664flJ NE XT I
6650 RETU ~rJ
We can have more than one cube in the scene. For example, if we rewrite
'scene3 ' as in listing 9.5, keeping all the other routines the same, we would get
figure 9.2. Note that the X, Y and Z values of the previous cube are overwritten
in the second call to 'cube'. Also, because we have the same ACTUAL to
OBSERVED matrix for both cubes (they have different SETUP to ACTUAL
matrices) we need to store Q so it can also be used for the second cube. Remem-
ber Q must premultiply the array P, which moves the second cube into the
ACTUAL position. The data for figure 9.2 are HORIZ = 9, VERT = 6, (EX, EY,
EZ) = (3, 2,1) and (DX, DY, DZ) = (0, 0, 0) .
164 Advanced Graphics with the Sinclair ZX Spectrum
Listing 9.5
6000 REM scene3/figure 9.2
6010 DIM X(8): DIM Y(8): DIM Z(8)
6020 DIM A(4,4): DIM B(4,4): DIM R(4,4): DIM Q(4,4)
6030 LET cube = 6500
6039 REM draw 1 ' st cube in OBSERVED posi ti on.
6040 GO SUB i dR3 : GO SUB look3
6050 GO SUB cube
6059 REM draw 2 'nd cube i n OBSERVED pos it i on.
6060 FOR I = 1 TO 4: FOR J = 1 TO 4
6070 LET ~(I, J)= R(I,J)
6080 NEXT J: NEXT I
6090 GO SUB i dR3
6100 LET TX = 3: LET TY = 1.5: LET TZ = 2: GO SUB t ran3: GO SUB mult3
6110 FOR I = 1 TO 4: FOR J = 1 TO 4
6120 LET A<I,J) = Q(I,J)
6130 NEXT J: NEXT I
6140 GO SUB mul t3
6150 GO SUB cube
6160 RETURN
/,~"/,,.'/]
/
"
./ /
l ....'
,.'
Figure 9.2
Exercise 9.1
Extend the routine 'cube' so that information about the size of a rectangular
block is input, enabling the routine to construct a block oflength LH, breadth
BH and height HT: multiply the x-values of the SETUP cube by LH/2, the y-
values by HT/2 and the z-values by BH/2.
Again it should be noted that the modular approach we have adopted may not
be the most efficient method of drawing three-dimensional pictures. We chose
this descriptive method in order to break down the complex situation into
manageable pieces. Once the reader has mastered these concepts, he should can-
nibalise our programs for the sake of efficiency. However, to show the value of
this modular approach we give another example, which illustrates just how
quickly programs can be altered to draw new scenes and situations.
Example 9.2
Wewish to view a fixed scene (for example, the one shown in figure 9.2) from a
variety of observation points.
In this case it is better to store the vertex coordinates of the scene in the
Orthographic Projections 165
ACTUAL position, rather than the OBSERVEDposition, and store the line
information in array L. The 'scene3' routine (listing 9.6) must first set NOV and
NOL to zero and then place the objects in their ACTUALposition using matrix
R =P. The construction routine 'cube' (listing 9.7) must therefore be altered to
update the data-base (but note the same routine could be used to store vertices
in their OBSERVED position, which needs only a different R = Q X P). Then
for each different view point and direction the 'scene3' routine must clear the
screen, set R to the identity matrix and call '100k3', and then call a special new
'drawit' routine (listing 9.8), which uses the matrix R (holding the values of Q,
the ACTUAL to OBSERVED matrix) to put the points in the OBSERVED
position and orthographically project them into arrays V and W (we cannot use
X and Y because this would corrupt our ACTUAL data-base). Routine 'drawit',
which was labelled in 'scene3', can then use the information in array L to draw
the picture on the screen.
Listing 9.6
6000 REM scene3/f igure 9.2 ( var i ety of vi ews )
6010 DIM X(16): DIM Y(16) : DIM Z(16)
6020 DIM V(16 ): DIM W(16): DIM L( 2,24)
6030 DIM A(4,4): DIM B(4,4): DIM R(4,4)
6040 LET cube = 6500: LET draw it = 7000
6050 LET NOV = 0: LET NOL = 0
6059 REM store 1 ' st cube in ACTUAL ( = SETUP) pos ition.
6060 GO SUB i dR3
6070 GO SUB cube
6079 REM st or e 2'nd cube i n ACTUAL pos it i on.
6080 LET TX = 3: LET TY = 1.5: LET TZ = 2: GO SUB tra n3: GO SUB mult3
6090 GO SUB cube
6099 REM loop through different viewing posi t ions.
6100 GO SUB i dR3 : GO SUB l ook3
6109 REM dr aw the two cubes in OBSERVED pos ition.
6110 CLS: GO SUB drawit
6120 GO TO 6100
6130 RETURN
Listing 9. 7
6500 REM cube/ vertices a nd l ine s (stored)
6501 REM IN NOV,NOL,X(NOV),Y (NOV),Z(NOV),L(2,NOL ),R (4,4)
6502 REM OUT: NOV,NOL,X (NOV),Y(NOV),Z(NOV),L (2,NOL )
6510 DATA 1,1,1, 1,1,-1, 1,-1,-1, 1,-1,1, -1,1,1, -1,1,-1, -1,-1,-1, -1,-1,1
6520 DATA 1,2, 2,3,3,4, 4,1, 5,6,6,7,7,8,8,5, 1,5, 2,6, 3,7, 4,8
6530 RESTORE cube
6540 LET NV = NOV
6549 REM i nput and store vertice s in posi t i on ( ACTUAL or OBSERVED ) us ing R.
6550 FOR I = 1 TO 8
6560 READ XX,YY,ZZ: LET NOV NOV + 1
6570 LET X( NOV) XX*R( 1, 1 ) + YY*R ( 1,2) + ZZ*R (1,3 ) + R(1,4 )
6580 LET Y(NOV) = XX*R(2,1) + YY*R( 2,2) + ZZ*R ( 2, 3) + R(2,4)
6590 LET Z(NOV) = XX*R(3,1 ) + YY*R(3,2 ) + ZZ*R( 3,3) + R(3,4)
6600 NEXT I
6609 REM i nput and store line i nf ormat ion.
6610 FOR I = 1 TO 12
6620 READ L1,L2: LET ~CL = NOL + 1
6630 LET L(1,NOL) = L1 + NV: LET L(2, NOL) = L2 + NV
6640 NEXT I
6650 RETURN
166 Advanced Graphics with the Sinclair ZX Spectrum
Listing 9.8
7000 REM drawit
7001 REM IN : NOV,NOL,X(NOV),Y(NOV),Z(NOV),L(2,NOL ),R(4,4 )
7~02 REM move vertices i nt o OBSERVED posit ion and draw object.
7010 FOR I =
1 TO NOV
7020 LET V(I) =
X(I)*R(1,1) + Y(I)*R(1,2) + Z(I)*R(1,3) + R(1, 4)
7030 LET we!) = X(I)*R(2,1) + Y(I)*R(2,2) + Z(!)*R(2,3) + R(2,4)
7040 NEXT I
7050 FOR ! =
1 TO NOL
7060 LET L1 = L(1,!): LET L2 = L(Z,J)
7~70 LET XPT = V(L1): LET YPT = W(L1): GO SUB mov et. o
7080 LET XPT = V(L2): LET YPT = W(L2): GO SUB lineto
7090 NEXT!
7100 RETURN
If the observer is travelling in a straight line and always looking in the same
direction, we need not even calculate Q each time, but simply initially manipulate
space so that the observer is looking along the a-axis, and then use the 'setorigin'
routine to move the observer instead! After you have gained expertise in drawing
three-dimensional projections, you should choose your construction and viewing
method with care. You will rarely need to go through the complete method given
in this chapter, there will always be short-cuts.
Exercise 9.2
Produce construction routines for a tetrahedron, pyramid, etc. For example,
(a) tetrahedron: vertices (1,1, 1), (1, -1, -1), (-1, 1, -1) and (-1, - 1, 1);
lines 1 to 2,1 to 3,1 to 4 , 2 to 3,2 to 4 and 3 to 4.
(b) pyramid with square of side 1 and height HT: vertices (0, HT, 0),(1, 0,1),
(1,0, -1), (-1,0, -1) and (-1 ,0, 1); lines 1 to 2 , 1 to 3, 1 to 4, 1 to 5, 2 to 3,
3 to 4, 4 to 5 and 5 to 1.
Exercise 9.3
Set up a line drawing of any planar object in the x/y plane; for example, the out-
line of an alphabetic character or string of characters, and view them in various
orientations in three-dimensional space. You can place such planar objects on the
side of a cube . All you need do is extend the 'cube' routine above to include extra
vertices and lines that define the symbols.
Thus far we have restricted our pictures to those of the simple cube. This is so
that the methods we give are not obscured by the complexity in .defining objects.
Our programs will work for any object provided it fits within the limitations of
our store (and time). For complex objects we merely extend the size of our
arrays, although some objects will have properties that enable us to minimise
store requirements. Consider the jet shown in figure 9.3 - it possesses two-fold
symmetry, which can be used to our advantage . We assume that the plane of
symmetry is the y/z plane, and so for every point (x, y, z) on the jet there is also
a corresponding point (-x, y, z). To draw figure 9.3 we use listings 9.1,9.2 and
Orthographic Projections 167
9.3 , together with a construction routine 'jet' that generates all the vertices of
the aeroplane with positive x-coordinates; thus information about only one-half
of the jet is stored. To construct the complete aeroplane we need also a 'drawit'
routine (listing 9.9), which draws one side of the jet, and then, by reversing the
signs of all the x -values, draws the other.
Figure 9.3
It is simple to construct these figures. Just plan your object in various sections
on a piece of graph paper, number the important vertices and note which pairs
of vertices are joined by lines. The coordinates values can be read directly from
the grid on the paper. The data for figure 9.3 are HORIZ = 160, VERT = 120,
(EX, EY, EZ) =(1,2,3) and (DX, DY, DZ) =(0, 0, 0) .
Bodies of Revolution
This far in our construction of objects we have relied on DATA to input all the
Figure 9.4
168 Advanced Graphics with the Sinclair ZX Spectrum
Listing 9.9
6000 RE M sce ne3! jet
6010 DIM X(37): DIM Y(37) : DIM Z(37)
6~20 DIM L( 2,46): DIM V(37): DIM W(37)
6030 DIM A(4,4): DIM B(4,4): DIM R(4,4)
604~ LET jet = 6500: LET drawit =7000
6050 GO SUB idR3 : GO SUB look3
6060 GO SUB jet
6070 GO SUB d r a ~i t
6080 RETURN
65~0 REM jet
6502 REM OUT: NOV,NOL,X(NOV),Y(NOV),Z(NOV),L(2,NOL)
6510 DATA 0,0,80, 0,~,64, 0,8,32, 4,8,32
6520 DAT A 8,4,32, 8,~,32, 4,-4,32
6530 DATA 0,8,-32, 4,8,-32, 8,4,-32, 8,~,-32
654~ DATA 4,-4,-32, 0,-4,-32, 8,~,24, 48,0,-32
6550 DATA 8,2,-32, 0,8,0, 2,8,-32, 0,32,-32
6560 DATA 28,-4,-24, 30,-2,-24, 32,-2,-24, 34,-4,-24
6570 DATA 32,-6,-24, 30,-6,-24, 28,-4,8 30,-2,8
6580 DATA 32,-2,8, 34,-4,8, 32,-6,8, 30,-6,8
659~ DATA 31,0,-24, 31,-2,-24, 31,-2,-12, 31,0,-12
6600 DATA 0,6,4~, 3,6,4~
6610 DATA 1,2, 2,3, 2,4, 2,5, 2,6, 2,7, 3,4
6620 DATA 4,9, 5,HI, 6,11, 7,12, 8,9, 9,HI, 10,11, 11,12
6630 DATA 12,13, 14,15, 15,10, 15,16, 14,16, 17,18, 17,19, 18,19
66~ DATA 20,21, 21,22, 22,23, 23,24, 24,25, 25,20, 26,27, 27,28
665~ DATA 28,29, 29,30, 30,31, 31,26, 20,26, 21,27, 22,28, 23,29
6660 DATA 24,30, 25,31; 32,33, 33,34, 34,35, 35,32, 36,37
6663 REM SETUP ver t ex and l ine i nf ormat i on for half the j et .
6700 LET NOV = 37: LET NOL = 46
6710 FOR I = 1 TO NOV: READ X(I),Y(I),Z(I): NEXT I
6720 FOR I = 1 TO NOL: READ L(1,I),L(2,I): NEXT I
6730 RETURN
7000 REM drawit! two halves of the jet
7001 REM IN : NOV,NOL,X(NOV),Y(NOV),Z(NOV),L(2,NOL),R(4,4)
7010 LET IS = 1
7019 REM loop through two halves of t he jet.
7020 FOR J = 1 TO 2
7030 FOR I = 1 TO NOV
704~ LET XX = IS*X(I): LET YY = Y(I): LET ZZ = Z(I)
7~49 REM put vert ices i n OBSERVED position.
7050 LET V(I) = XX*R(1,1) + YY*R(1,2) + ZZ*R(1,3) + R(1,4)
7060 LET WeI) = XX*R(2,1) + YY*R(2,2) + ZZ*R(2,3) + R(2,4)
7070 NEXT I
7080 FOR I = 1 TO NOL
7090 LET L1 = L(1,I): LET L2 = L(2,I)
7100 LET XPT = V(L1): LET YPT = W(L1): GO SUB moveto
7110 LET XPT = V(L2): LET YPT = W(L2): GO SUB lineto
7120 NEXT I
7130 LET IS = -1
71 4~ NEXT J
7150 RETURN
information about lines and vertices. We consider now a type of object where
only a small amount of information is required for a quite complex object -this
is a body of revolution, an example of which is shown in figure 9.4.
Orthographic Projections 169
The method is simply to create a defining sequence of NUMV lines in the x/y
plane through the origin ; we call this the definition set. Then we revolve this set
about the vertical (y-axis) NUMH-1 further times to create new vertical sets.
The NUMV lines in the definition set are formed by joining the NUMV + 1
vertices (S(I), T(I), 0) (where I ~ I ~ NUMV + 1) in order. From this we generate
NUMH different vertical sets, the J t h vertical set is the definition set rotated
through an angle PHI + 21T(J - l)/NUMH about the vertical y-axis, for some in-
put value PHI (fjJ) . As well as the set of NUMH x NUMV vertical lines we intro -
duce horizontal lines also. We consider a single point (S(I), T(I), 0) at the end of
a line segment in the definition set: as we rotate about the vertical axis it moves
into NUMH positions (provided that the point is not on the axis of rotation)
Listing 9.10
60~~ REM s cene3/ s phe r oi d
6~10 DIM X(32) : DIM Y(32)
6020 DIM A(4,4): DIM B(4,4): DIM R<4,4)
6030 DIM 5(16): DIM T(16)
6040 LET revbod = 650~
6050 INPUT "NUM3ER OF HORIZONTAL LINES",NUMH
6060 INPUT "NUM3ER OF VERTICAL LINES",NUMII
6070 INPUT "ANGLE PHI " ; PHI
6080 LET THETA = PI/2 : LEI TD = Pl/NUMII
6089 REM generate def inition set.
6090 FOR I = 1 TO NUMII + 1
610~ LET 5(1) = COS THETA: LET T<I) = SIN THETA
611~ LET THETA = THETA + TD
6120 NEXT I
6130 GO SUB idR3: GO SUB Look3
6140 GO SUB revbod
6150 RETURN
Exercise 9.4
Experiment with this technique, any line sequence will do . Try an ellipsoid : this
is essentially the same as the spheroid except that the definition set is produced
170 Advanced Graphics with the Sinclair ZX Spectrum
Listing 9.11
650~ REM revbodl body of revoLut ion
6501 REM IN : PHI,NUMH,NUMV,S(NUMV+1>,HMJMV+1>,R(4,4)
6509 REM create f irst verticaL set.
6511' LET THETA = PHI: LET TO = PI*2/NUMH
6520 LET N1 = NUMV + 1: LET C = COS PHI: LET S =SIN PHI
6530 FOR 1 = 1 TO N1
6540 LET XX = S(I)*C: LET YY = T(I): LET ZZ = S(I)*S
6550 LET XCI) = XX*R(1,1) + YY*R(1,2) + ZZ*R(1,3) + R(1,4)
6560 LET Y(I) = XX*R(2,1) + YY*R(2,2) + ZZ*R(2,3) + R(2,4)
6570 NEXT J
6579 REM Loop through second ver ti ca L set~
6580 FOR J = 1 TO NUMH
6590 LET THETA = THETA + TO : LET C = COS THETA: LET S = SIN THETA
6601' FOR 1 = 1 TO N1
6610 LET XX = S(I)*C: LET YY = T(I): L ET ZZ = S(I)*S
6620 LET X(I + N1) = XX*R(1,1) + YY*R(1,2) + ZZ*R(1,3) + R(1,4)
6630 LET YO + N1) = XX*R(2,1) + YY*R(2,2) + ZZ*R(2,3) + R(2,4)
6640 NEXT 1
6649 REM draw Lines i n first verticaL set.
6650 LET XPT = X(1): LET YPT = Y(1): GO SUB moveto
6660 FOR ! = 2 TO N1
6670 LET XPT = =
XCI) : LET YPT Y(I): GO SUB Lineto
6680 NEXT 1
6689 REM joi n corresponding horizontaL vertices on the two verticaL sets.
6690 FOR r = 1 TO N1
670~ LET XPT = XCI): LET YPT = Y(I) : GO SUB moveto
6710 LET XPT = X(I + N1) : LET YPT = YO + N1): GO SUB Lineto
6719 REM copy second verticaL set into first and repeat process.
6720 LET XCI) = XPT: LET Y(I) = YPT
6731' NEXT 1
674f1 NEXT J
6750 RETURN
Figure 9.5
Orthographic Projections 171
can move in a regular manner ; that is, drop by the same amount with each
rotation through 21T/NUMH. Now, of course, the lines can make more than one
complete rotation about the axis, see figure 9.5. Write a program to implement
a body of rotation.
Complete Programs
From now on we shall refer to listings 3.4 ('angle'), 8.1 ('mult3 ' and 'idR3 '),
8.2 ('tran3'), 8.3 ('scale3'), 8.4 ('rotJ'), 9.1 ('100k3') and 9.2 ('main program') as
'lib3' .
I. 'lib l ', 'lib3' and listings 9.3 ('scene3') and 9.4 ('cube'). Data required:
HORIZ, VERT, (EX, EY, EZ) and (DX, DY, DZ). Try 6,4, (1,2,3),
(-1,0 ,1).
II. 'lib1" 'lib3' and listings 9.5 ('scene3') and 9.4 ('cube'). Data required:
HORIZ, VERT, (EX , EY, EZ) and (DX, DY, DZ). Try 9,6, (1, 2, 3),
(-1,0,1). Make systematic changes to one of these input values and keep
all the other parameters fixed.
III. 'lib 1" 'lib3 ' and listings 9.6 ('scene3'), 9.7 ('cube') and 9.8 ('drawit'). Data
required : HORIZ, VERT , and then repeated input of (EX, EY, EZ) and
(DX, DY, DZ). Try 9,6, then (1,2,3), (-1,0, 1); (3,2,1), (0, 0,1). Again
make systematic changes to one of the input parameters.
IV. 'lib 1" 'lib3' and listing 9.9 ('scene3', 'jet' and 'drawit'). Data required:
HORIZ , VERT , and then repeated input of (EX, EY, EZ) and (DX, DY,
DZ). Try 160,120, then (I, 2, 31) , (-1,0,30); (3, 2,20), (0, 0, 21).
Again make systematic changes to one of the input parameters.
V. 'lib1" 'lib3' and listings 9.10 ('scene3') and 9.11 ('revbod'). Data required:
HORIZ, VERT , NUMH, NUMV (~15) , PHI, (EX, EY, EZ) and (DX, DY,
DZ). Try 3.2, 2.2,10,10, I, (I, 2, 3), (0, 0, 0); (3, 2,1), (0, 0, 0).
10 Simple Hidden Line and Surface
Algorithms
Having drawn a cube and other wire objects we soon become irritated by the lack
of solidity in the figures. We would like to consider solid objects, in which case
the facets at the front of the object will obviously restrict the view of the facets
(and boundary lines) at the back. In order to draw pictures of such objects we
have to introduce a hidden surface algorithm ; or a hidden line algorithm if we
wish to draw all, but only, the visible lines on the object. There are many, many
such algorithms - some elementary for specially restricted situations, others
very sophisticated for viewinggeneral complicated scenes. The time limitations
of microcomputers bar us from implementing the very complex algorithms . In
the case of the Spectrum we have also the limitation that we cannot draw more
than two colours in a character block , which therefore restricts us to line draw-
ings. Nevertheless, by limiting the types and number of objects in the scenes, it
is possible to get most acceptable pictures. In chapter 12 we discuss a relatively
complex algorithm , but here we consider two special types of scene - we use
the properties implicit in these special configurations to minimise the work
needed to discover which surfaces and lines are hidden. Later in this chapter we
shall give a simple method for drawing mathematically defined three-dimensional
surfaces, but to start we consider an algorithm for drawing a single solid convex
body in three -dimensional space.
For our work on hidden line and surface algorithms we choose to define a
scene by storing the NOV vertices of objects in the scene (in the OBSERVED
position) in arrays X, Y and Z as usual. However we shall now use facet informa-
tion rather than line. The NOF facets are stored in an array F (we need also the
array H for the number of edges on each polygonal facet), and to save space
insist that no polygonal facet has more than six edges. Should we need more
edges, then the facet must be broken down into a set of smaller polygons . In
order to make the hidden surface algorithm easier we impose a restriction on the
order of vertices within the array F. The vertices must be stored in the order in
which they occur around the edge of the facet, and when viewed from the out-
side of an object they must be in an anti-clockwise orientation. Naturally from
the inside the vertices taken in this same order would appear clockwise. We shall
assume also that all lines are the junction of two facets. Individual lines not
related to facets must be added as trivial two-sided facets .
Simple Hidden Line and Surface Algorithms 173
Once we have planned our object in terms of vertices and facets , how do we
check that the facets are actually anti-clockwise? Simply write a program! The
orientation of any convex polygon can be calculated from any three of its ver-
tices taken in order, and so we need consider only an ordered triangle of vertices
from the facet. Wehave already seen, in chapter 7, a method for calculating the
orientation of a two-dimensional triangle. Our problem is solved if we can reduce
the three-dimensional situation to two dimensions.
For simplicity we assume that all objects are SETUP about and containing the
origin. We insist also that the infinite planes containing the facets on the surface
of an object do not pass through the origin. Then we rotate space so that one of
the vertices of the triangle in question lies on the negative z-axis (compare with
routine '100k3', listing 9.1). Since we assume the origin is inside the object and
the eye is outside, all we need do is project the transformed triangle back on to
the x/y plane (that is, ignore the a-coordinates) and treat it like a two-dirnen- .
sional triangle (in fact one of the three vertices will be (0,0)). Listing 10.1 is our
solution of the problem.
Listing 10.1
Exercise 10.1
Rewrite the wire-figure routines of the last chapter using the assumption that the
data are given as vertices and anti-clockwise polygonal facets , and not as lines.
Check your facet data with the above program . The line information is still there
of course, implicit in the facet data - they are the edges of the facet considered
as pairs of vertices. Within this information each line occurs twice, once on
each of two neighbouring facets . We do not want to waste time drawing lines
twice! Because of the anti-clockwise manner of constructing the figures we note
that if a line joins vertex I to vertex J on one facet then the equivalent line on
the neighbouring facet joins vertex J to I. So for wire figures stored as facets we
shall draw lines from vertex I to vertex J if and only if I < J .
A finite convex body is one in which any line segment joining two points inside
the body lies totally within the body : a direct extension of the definition in
two-dimensional space. It is automatically closed, and thus it is impossible to get
inside the body without crossing through its surface. We orthographically project
all the vertices of the object on to the view plane, noting that a projection of a
convex polygon with n sides in three-dimensional space is an n-sided convex
polygon (or degenerates to aline) in the view plane. Taking the projected ver-
tices of any facet in the same order as the original, we find that either the new
two-dimensional polygon is in anti-clockwise orientation, in which case we are
looking at the outside of the facet, or the new vertices are clockwise and we are
looking at the underside . Since the object is closed we are able to see only the
outside of facets ; the view of their underside is blocked by the bulk of the object.
Therefore we need draw only the anti-clockwise polygonal facets - a very simple
algorithm, which can be implemented in either construction or 'drawit' routines.
For example, an adjusted construction routine 'cube' for eliminating the
hidden lines from an orthographic picture of a cube is given as listing 10.2. Here
we do not store the facets , but instead READ the information from DATA and
draw the visible facets immediately. This program was used to produce figure
10.1, a hidden line version of figure 9.1d . We use all the routines from the last
chapter that were used to draw figure 9.1d, except of course.for the construction
routine that sets up the data as vertices and facets, and draws the object (this
replaces listing 9.4 in the program for drawing figure 9.1 d). Naturally we use the
same data that were used for figure 9.1 d.
Exercise 10.2
Change listing 10.2, so that it can draw a rectangular block oflength LH, breadth
BH and height HT, where LH, BH and HT are input parameters to the routine.
Then draw a hidden line picture of it. Draw hidden line pictures of tetrahedra,
pyramids, octahedra, etc. Add extra parameters to distort these figures so that
they are no longer regular, but are still convex.
Simple Hidden Line and Surface Algorithms 175
Listing 10.2
6500 REM cube/facets (not stored) hidde n l ine eLi mi nat i on
6501 REM IN : R(4,4)
65111l DATA 1,1,1, 1,1,-1, 1,-1,-1, 1,-1,1, -1,1,1, -1,1,-1, -1,-1,-1, -1,-1,1
6520 DATA 1,2,3,4, 5,8,7,6, 1,5,6,2, 2,6,7,3, 3,7,8,4, 4,8,5,1
65311l RESTORE cube
6539 REM pLace vertices in OBSERVED pos ition.
6540 FOR I = 1 TO 8
6550 READ XX,YY,ZZ
6560 LET X(l) = XX*RC1,1) + YY*R(1,2) + ZZ*R(1,3) + R(1,4)
6570 LeT Y(I) = XX*RC2,1) + YY*RC2,2) + ZZ*RC2,3) + R(2,4)
6580 NEXT I
6590 FOR I = 1 TO 6
6599 REM REA D fa cEt i nf 0rma t i on and draw i t if oriented anti-cL ockwise.
6600 READ F1,F2,F3,F4
661~ LET VX1 = XCF2)-X(F1): LET DY1 = Y(F2)-Y(F1)
66211l LET DX2 = XCF3)-X(F2): LET DY2 = YCF3)-Y(F2)
6630 IF DX1*DY2 - DX2*DY1 < 0 THEN GO TO 6690
6640 LET XPT = XCF1): LET YPT = YCF1): GO SUB moveto
6650 LET XPT XCF2): LET YPT = YCF2): GO SUB Lineto
6660 LET XPT XCF3) LET YPT YCF3 ): GO SUB Lineto
66711l LET XPT XCF4) LET YPT Y(F4): GO SUB Lineto
66811l LET XPT XCF1> LET YF'T Y<F1): GO SUB Lineto
6690 NEXT I
6711l1ll RETlif'to.
~\
\\
.
\ .
\
,\ "\
, I
"\ I
II 1I ~-' -" 1
.
, _~.~. I
I\ .I"~'
,
-" ' --
' _ _ ._~ .. ~ ~_~_r ..- - ""--
Figure 10.1
Bodies of Revolution
surface version of figure 9.4 (and uses the same input data). Again, because of
the modular design of our programs, all the routines needed to draw figure 10.2,
except 'revbod' , are the same as those given in chapter 9. Now, however , we
must deal solely with convex bodies of revolution.
Figure 10.2
As the routine rotates the definition set of lines about the vertical axis, it
stores the vertices of two consecutive vertical sets of lines. These form the
vertical edges of one slice of facets. The vertices on these facets are immediately
transformed by R (the SETUP to OBSERVED matrix) and stored in arrays X
and Y. In such a configuration of pairs of vertical lines the first set of vertices
have indices from 1 to NUMV + 1 (= Nl), and the second from Nl + 1 to 2*Nl.
The I th facet is bounded by four lines, two vertical joining vertex I to I + 1, and
1+ Nl to I + Nl + 1, and two horizontal joining I to I + Nl, and I + 1 to I + Nl
+ 1. Adjustments must be made if one of the original vertices is on the axis of
rotation , in which case the quadrilateral degenerates to a triangle. The order of
vertices in each facet is carefully chosen so that they are in anti-clockwise
orientation when viewed from outside the object. This allows us to use our
simple algorithm to draw the object with the hidden lines suppressed. This
technique was used also to draw figure I.1 in the introduction.
Exercise 10.3
Experiment with this technique . Any initial set of lines will do , provided that it
starts and ends on the vertical axis and the polygon thus formed in the x/y plane
is convex.
Simple Hidden Line and Surface Algorithms 177
Listing 10.3
The call for pictures of convex solids is limited, so we now look at one type of
non-convex figure that can be drawn using information about its special form.
We consider the construction of a restricted type of three-dimensional surface in
which the y-coordinate of each point on the surface is given by a single-valued
function of' of the x-coordinate and z-coordinate of that point; of' will be includ-
ed as a routine in the program - one such example is given in listing lOA, the
functiony = 4 x SIN (XZ)/XZ where XZ = V(x 2 + Z2) shown in figure 10.3 .
The data required were HORIZ =32, VERT =22, (EX, EY, EZ) =(3, 2, 1),
(DX, DY, DZ) = (0 , 0, 0) , NX = NZ = 16, XD = ZD = -10 and XT = ZT = 10.
178 Advanced Graphics with the Sinclair ZX Spectrum
Listing 10.4
6000 REM scene3
6010 DIM A(4,4): DI~ B(4,4): DIM R(4,4)
6020 LET surface = 6500: LET dra wlin = 7000: LET f 7200
6030 GO SUB i dR3 : GO SUB look3
6040 GO SUB surface
6050 RETU RN
6500 REM surface
6501 REM IN : R(4,4)
6510 DIM 0(256)
6520 INPUT "NX,XMI N,XMAX ";NX;",";XMI N;",";XMA X
6530 INPUT "NZ,ZMIN,ZMAX ";NZ;",";ZMIN;",";ZMAX
6540 LET XCI F = (XMA X - XMIN) / NX
6550 LET ZDIF = (Z~A X - ZMIN)/NZ
6559 REM draw zero'th set of fixed-x line s .
6560 LET XX = XMAX: LET ZZ = ZMIN: GO SUB f
6570 LET XA = XX*R(1,1> + YY*R(1,2) + ZZ*R(1,3) + F.< 1,4)
6580 LET YA = XX*R(2,1) + YY*R(2,2) + ZZ*R(2,3) + R(2,4)
6590 FOR J = 1 TO NZ
6600 LET ZZ ZZ + ZDIF: GO SUB f
6610 LET XB = XX*R(1,1) + YY *R(1, 2) + ZZ*R(1,3) + R(1,4)
6620 LET YB = XX*R(2,1) + YY*R(2,2) + ZZ*R (2,3) + R(2,4)
6630 GO SUB draa l i n
6640 LET XA XB: LET ~A = YB
6650 NEXT J
66~ 9 REMdraw zero'th set of f ixed-z li nes.
6660 FOR J = 1 TO NX
6670 LET XX = XX-XDIF: GO SUB f
6680 LET XB = XX*R(1,1) + YY*R(1,2) + ZZ*R(1,3) + ~(1 , 4 )
6690 LET VB = XX*R(2, 1) + YY*R(2,2) + ZZ*R(2,3) + R(2,4)
6700 GO SUB drawl in
6710 LET X ~ = XB: LET YA = YB
6720 NEXT J
6729 REM move x v a l u e ~ ta ck ;n NX s t eps .
6730 LET XS = XMAX
6740 FO R I = 1 TO NX
6748 REM draw vi si bl e parts of one f rom eac h of the f ixed-z li ne s :
6749 t he x values vary fr om (I-1 )st x- li ne to t he 1 ' t h.
6750 LET ZS = ZMA X
67 6 ~ FOR J = 1 TO NZ
6770 LET ZS = ZS - ZDIF
6780 LET XX = XS: LET ZZ = ZS : GO SUB f
6790 LET XA = XX*R(1,1) + YY*R(1,2) + ZZ*R(1,3) + R(1,4)
6800 LET YA = XX*R(2,1) + YY*R(2,2) + ZZ*R(2,3) + R(2,4)
6810 LET XX = XS-XDIF: GO SUB f
6820 LET XB = XX*R(1,1) + YY*R(1,2) + ZZ*R(1,3) + R(1,4)
6830 LET YB = XX*R(2,1) + YY*R(2,2) + ZZ*R(2,3) + R(2,4)
6840 GO SUB draw lin
6850 NEXT J
6859 REM draw visible parts of the fixed-x lines.
6860 LET ZZ ZMIN: GO SUB f
6870 LET XA = XX*R(1,1) + YY*R(1,2) + ZZ*R(1,3) + R(1,4)
6880 LET YA = XX*R(2,1) + YY*R(2,2) + ZZ*R(2,3) + R(2,4)
6890 FOR J = 1 TO NZ
6900 LET ZZ = ZZ + ZDIF: GO SUB f
6910 LET XB = XX*R (1,1) + YY*R(1,2) + ZZ*R(1,3) + R(1,4)
6920 LET VB = XX* R(2, 1) + YY*R(2,2) + ZZ*R ( 2,3) + R(2,4)
6930 GO SUB drawl in
6940 LET XA = XB: LET YA = YB
6950 NEXT J
6960 LET XS = XS-X DIF
6970 NE XT r
(,c:l:ll RETU RN
Simple Hidden Line and Surface Algorithms 179
Figure 10.3
(NX + 1) x (NZ + 1) vertices (X, Z) in the grid that can be identified by the pair
of integers (i , j)
The equivalent point on the surface is (X, Y, Z) where Y = [(X, Z) . Every one of
the (NX + 1) x (NZ + 1) points generated in this way is joined to its four
immediate neighbours along the grid (that is, those with an equal x-value or z-
value), unless it lies on the edge, in which case it is joined to three, or in the case
of corners to two, neighbours . The grid lines can be considered as NX + 1 sets of
different fixed-x line segments and NZ + 1 sets of fixed-z line segments . For
example the Ith fixed-x set of lines (0 < I < NX) consists of NZ lines joining pairs
of points equivalent to the grid points (I, J) to (I, J + 1), where 0 < J < NZ - 1.
The surface can undulate, so not all the lines need be visible from a given view
point. We devise a very simple method to eliminate the hidden lines by working
from the front of the picture to the back.
To simplify the algorithm we assume that the eye is always in the positive
quadrant (that is, EX > 0 and EZ > 0), and that the eye is always looking at the
origin (OX =DY =DZ =0). If the function is non-symmetrical and we wish to
view it from another quadrant, then we simply change the sign of x and/or z in
the function . We can then transform the surface into the OBSERVED position.
In this position we first orthographically project the front edge, the two grid
line with x = XT and z = ZT, on to the view plane. That is, the zeroth set of
fixed-x lines and the zeroth set of fixed z-lines, We now work from the front to
the back in NX steps. In the Ith step (1 < I < NX), we draw first the visible parts
of one line segment from each of the NZ fixed-a sets;the J th such line (1 < J <NZ)
joins the points equivalent to the grid points (I - 1, J) to (I, J) . That is, we join
the corresponding points on the (I - l)th fixed-x line to the Ith, starting at the
zeroth fixed-z line and working backwards. Then we draw the visible parts of the
Ith set of fixed-x line segments. This is all programmed in routine 'surface' .
As yet we have not explained how to draw the visible parts of the lines :
routine 'drawlin'. We define an array 0 of256 values, one for each column of
pixels across the screen . When we first draw the zeroth sets of fixed -x and
fixed-a lines, we put the pixel values of the points on these lines in the array D.
We calculate the row/column values for every pixel on a given line segment and
draw it if and only if the row value for a column is greater than the value stored
in D. Whenever a pixel is added to the diagram then the value of array 0 is
altered accordingly . This method furnishes us with a hidden line elimination
algorithm for mathematical surfaces because of the order in which we consider
the lines. If we move out of the positive quadrant, or let the scale of the figure
exceed the size of the graphics area, then we shall incur errors.
Simple Hidden Line and Surface Algorithms 181
Exercise 10.4
Change the functions 'f' used by this program. For example, use f= 4 x SIN(t)
where t = V(x 2 + Z2).
Exercise 10.5
The above program does not draw the underside of the surface if it shows below
the zeroth ftxed -x/z lines. Alter listing 1004 to correct this shortcoming . Defme
another array E(256) that initially holds information on the nearest edges, and
whenever pixels on the line segments go below the values stored in E then the :
pixel is drawn and the value of E altered. Can we use the same order for drawing
the lines?
Complete Programs
1. 'lib3 ' and listing 10.1. Data required : the vertex coordinates of a triangle
(X(I), Y(I), Z(I)), where 1 ~ I ~ 3. Try (1,0,1) , (1,1,0) and (0, I, 1):
also the same vertices in a different order (1, 1, 0) , (1, 0, 1) and (0, 1, 1).
II. 'lib 1" 'lib3' and listings 9.3 ('scene3') and 10.2 ('cube'). Data required :
HORIZ, VERT , (EX, EY, EZ), (DX, DY, DZ). Try 9, 6, (1,2,3), (0, 0, -1).
III . 'lib 1" 'lib3' and listings 9.10 ('scene3 ') and 10.3 ('revbod'). Data required :
HORIZ, VERT, NUMH, NUMV (~ 15), PHI, (EX, EY, EZ), (DX, DY, DZ).
Try 3.2,2 .2, 10, 10, 1, (1 ,2,3), (0, 0, -1).
IV. 'lib 1" 'lib3 ' and listing 1004 ('scene3 ', 'surface', 'draw lin' and 'f") . Data
required: HORIZ , VERT , (EX, EY, EZ), (DX, DY, DZ), NX, XB, XT, NZ,
ZB, ZT. Try 30,20, (1 , 2 , 3), (0 , 0, 0),16, -8 , 8,16, -8,8 .
11 Perspective Projections
We have seen that the orthographic projection has the property that parallel
lines in three-dimensional space are projected into parallel lines on the view
plane. Although very useful, such views do look odd! Our brains are used to the
perspective phenomenon of three-dimensional space, and so they attempt to
interpret orthographic figures as if they are perspective views. For example , the
cubes of figures 9.1 and 10.1 look distorted.
So it is essential to produce a projection that displays perspective phenomena
(that is, parallel lines should meet on the horizon); an object should appear
smaller as it moves away from the observer. The drawing-board methods devised
by artists over the centuries are of no value to us. Three-dimensional coordinate
geometry and the concept of ACTUAL to OBSERVED positions, however
furnish us with a relatively straightforward technique.
Figure 11.1
PerspectiveProjections 183
We let the perspective plane be a distance d from the eye (variable PPD in
later programs). Consider a point P == (x,y, z) in space that sends a ray into the
eye. We must calculate the point where this line cuts the view plane (the z = d
plane); suppose it is the point P' == (x',y', d) . Let us first consider the value of
y' by referring to figure 11.2. By similar triangles we see that y'[d = ylz , that is
y' = Y x dfz : Similarly x' = x x dlz : Hence P' == (x x dlz , y x dfz , d) . Since the
view plane is identified with the x/y coordinate system of the graphics screen,
we can ignore the z = d coordinate.
Example 11.1
Calculate the perspective projection of a cube with eight vertices (0, 0, 4) +
(± 1, ± 1, ± 1) on the perspective plane z =4, where the eye is origin and the
straight-ahead ray is the positive a-axis.
The space is defined so that the scene is in the OBSERVED position. We can
184 Advanced Graphics with the Sinclair ZX Spectrum
- - - -- z -- - - -
p
p' - - I
y
---d---
Figure 11.2
calculate the projections of the eight vertices using the above method. For
example, (1, 1, 3) is projected to (I x 4/3,1 x 4/3,4)=(4/3,4/3,4)~(4/3,4/3)
on the screen. So we get the eight projections
til til
.r-f •.-1
>< . ><
ro . (1j'
I'
I .
>,. >,:
.
x-a?,~:; , '8 ',,,,
~
. ~ . . x-axi s
(a) (b)
Figure 11.3
(1) The perspective transformation of a straight line (I' 3 say) is a straight line
(r2 say). This is obvious because the origin (the eye) and the line r 3 form a
plane (n say) in three-dimensional space and all the rays emanating from points
on r 3 lie in this plane. (If the line enters the eye, n degenerates into aline).
Naturally n cuts the perspective plane in a line r 2 (or degenerates to a point)
Perspective Projections 185
and so the perspective projection of a point on the original line r 3 now lies on
the new line r 2. It is important to realise that a line does not become curved on
perspective projection .
(2) The perspective transformation of a facet (a closed sequence of coplanar line
segments) is a facet in the perspective plane. If the facet is an area bounded by n
coplanar line segments, then the transform of this facet is naturally an area in the
Z = d plane bounded by the transforms of the n line segments. Again note that no
curves are introduced in this projection: if they were, then the task of producing
perspective pictures would be far more complicated.
(3) The projection of a convex facet is also convex. Suppose facet F I is project-
ed on to facet F2. Since the projection of a closed facet is also closed and lines
go into lines, then points inside F I are projected into points inside F2. Suppose
F 2 is not convex: then there exist two points PI andp2 inside F 2 such that the
line joining them goes outside this facet. Hence there is at least one point P on
the line outside F 2 . If PI and P2 are projections of points q I and q2 from F I,
then P is the projection of some point q on the line joining q I and q2 . Since the
F I is convex then q must be inside F I and thus P must be inside F2: a contra-
diction, and our proposition is proved.
(4) All infmitely long parallel lines appear to meet at one point, their so-called
vanishing point . If we take a general line (with base vector p) from a set of
parallel lines with direction vector h
where zh > 0; then the perspective transform of a general point on this line is
(
(Xp + IlXh) x ~, (yp + IlYh) x d)
(zp + IlZh) (zp + IlZh)
As we move along the line towards large a-coordinates (that is, as Il ~ 00), then
the line moves towards its vanishing point, which is therefore given by
(d x Xh/Zh' d x Yh/Zh). This vanishing point is independent of P, the base point
of the line, and hence all lines parallel to the direction h have the same vanish-
ing point. Of course the case zh < 0 is ignored because the line would disappear
outside the cone of vision as Il ~ 00.
(5) The vanishing points of all lines in parallel planes are collinear. Suppose that
the set of parallel planes has a common normal direction n == (x n ,Yn, zn)' If a
general line in one of these planes has direction h == (Xh' Yh. zh), then h is per-
186 Advanced Graphics with the Sinclair ZX Spectrum
pendicular to n (all lines in these planes are perpendicular to the normal to the
plane n). Thusn • h = 0, which in coordinate form is
dividing by Zh gives
xn X X +Yn X Y +d x zn = 0
Example 11.2
Find the vanishing points of the edges of the cube in example 11.1, and of the
diagonals of its top and bottom planes.
We divide the twelve edges of the cube into three sets of four edges, each set
being parallel to the x-axis,y-axis and z-axis respectively and so with directional
vectors (1, 0, 0), (0, 1, 0) and (0, 0 , 1). The first two sets have zero Z -values, and
so their extended edges disappear outside the cone of vision and are ignored,
whereas the third direction has vanishing point (4 x 0/1 ,4 x 0/1) == (0, 0) on the
view plane. On the top and bottom faces the diagonals have directions (1,0, 1),
the major diagonal, and (-1 ,0, 1), the minor diagonal. The major diagonal on
the top plane is (-1 , 1,3) + 11(1 ,0, 1), and so the vanishing point is (4 x 1/1,
4 x 0/1) == (4 , 0). The minor diagonal on the top plane is (1 , 1,. 3) + Il( -1 ,0 , 1)
and the vanishing point is (4 x -1/1 , 4 x 0/1) == (-4, 0). By similiar calculations we
find that the vanishing points of the major and minor diagonals on the lower face
are also (4 , 0) and (-4 , 0) respectively. The relevant edges are extended to their
vanishingpoints in figure 11.3b. Note that all the lines mentioned lie in the two
parallel planes (the top and bottom faces of the cube) and so the vanishing points
should be collinear: since (4, 0), (0, 0) and (-4,0) all lie on the x-axis, this is
obviously true . It can be shown similarly that the vanishing points of the diagon-
als of the side faces lie on a vertical line through the origin.
Exercise 11.1
Draw a perspective view of a tetrahedron with vertices (1, 1,5), (1, - 1, 3),
(- 1, 1, 3) and (-1 , -1 , 5). Find the vanishing points (inside the cone of vision)
of lines that join pairs of mid-points of edges of the tetrahedron.
Perspective Projections 187
The main program for drawing a perspective view of any scene is the same as
that for the orthographic view, namely listing 9.2. Again the overall scene is
created by a call to a routine 'scene3' similar to those discussed in chapter 9. We
shall often need to calculate explicitly the ACTUAL to OBSERVED matrix , so
that the eye is in the OBSERVED position at the origin looking along the positive
z-axis. This is achieved by routine '100k3', given in chapter 9 (listing 9.1) . Calls
are made to construction routines, each having a matrix R as parameter. Finally
the figure must be drawn, inside the construction routines or in a 'drawit'
routine .
The only difference between the program that draws a perspective view and
the program of chapter 9 (orthographic view) is in the calculation of the co-
ordinates of the projected image on the view plane. Unlike the orthographic, in
the perspective projection the coordinates on the view plane cannot be identified
with the x-value and y-value of the point in the OBSERVED position. We need
to store the perspective transformation of the vertices in the arrays V and W: the
Ithvertex (X(I), Y(I), Z(I)) in the OBSERVED position is projected to (V(I),
W(I)). The values in arrays V and W are given by
where the value of PPD is set to 3*VERT ; the reason for this equation is given in
the next section. The calculation of V and W can be made in the construction
routine in the 'scene3' or 'drawit' routines; this simply depends on the scene
being considered .
Example 11.3
We draw a flxed scene (the two cubes described in example 9 .2) in perspective
from a variety of observation points, setting HORIZ = 9 and VERT = 6. The
necessary 'scene3' routine is given in listing 11.1 below; note that this calculates
PPD (compare with listing 9 .6). This listing places the group of cubes in their
ACTUAL position using the 'cube' routine of listing 9.7, and then loops through
a number of different OBSERVER positions. For each time through the loop we
call '100k3' , which requires (EX , EY, EZ) and (DX, DY, DZ) to calculate the
ACTUAL to OBSERVER matrix. Then the perspective 'drawit' routine (listing
11.2) is called. This uses the matrix to transform the vertices from their (stored)
ACTUAL position to the OBSERVER position, and places the projected vertex
coordinates in arrays V and W, according to the above equations. The routine
can then finally draw the edges of the cubes in perspective.
Figure 11.4 was drawn using (EX, EY, EZ) == (15 ,10,5) and (DX, DY, DZ) ==
(0,0,0). Compare this with the orthographic view of the same scene given in
figure 9.2.
188 Advanced Graphics with the Sinclair ZX Spectrum
Listing 11.1
6000 REM scene3/figure 9.2 (variety of vi ews)
6010 DIM X(16): DIM Y(16): DIM Z(16)
6020 DIM V(16): DIM W(16): DIM L(2,24)
6030 DIM A(4,4): DIM B(4,4): DIM R(4,4)
6040 LET cube = 650V!: l.rT cr av t t = 7000
6050 LET PPD = 3*VERT: LF.T nov = 0: LET NOL = 0
6059 REM pLace two cubes in ACTUAL position.
6060 GO SUB idR3
6070 GO SUE' C'"re,
6080 LET TX = 3: LET TV = 1.5 : LET rz = 2: GO SUB tran3: GO SUB muLt3
6090 GO SUB cube
6099 RE~ Loop through variety of views.
6100 GO SUB idR3 : GC HB Look3
6110 CLS : GO SUB drawit
6120 GO TO 6Hl0
6130 RETURN
Listing 11.2
7000 REM drawitf perspect ive
7001 REf', III : f'F'(', NOV, NOL,XCNOV) ,Y(NOV) ,ZCNOV) ,LCt',NOU,R(4,4)
7009 REM put vert ices in OBSERVED position.
7010 FOR I = 1 TO NOV
7020 LET XX = X(I )*~(',1) + Y(I)*R(1,2) + Z(I)*R(1,3) + R(1,4)
7030 LET YY = X(I)*R(2,1) + Y(I)*R(2,2) + 2(I)*R(2,3) + R(2,4)
7040 LET ZZ = X(I)*R(3,1) + Y(I )*~( ~,2) + Z(I)*R(3,3) + R(3,4)
7049 REM per spectt ve proj ecti on.
7050 LET V(I) = xX*PPD/ZZ
7060 LET Wei) = YY*PPD/ZZ
7070 NEXT I
7079 R~r·: ,.'r'aW l ines.
7080 FOR I = 1 TO NOL
7090 LET i.t = L<1,I): LET L2 = U2,n
7100 LET XPT = V(L1 ): LFT ~PT = W(L1): GO SUB moveto
7110 LET XPT = V(LZ ): l r: YfT = W(L2): GO SUB Li n~ t o
7120 NEXT I
7130 RETl'FN
I
l
'"It
------ - II
l
Ji
Figure 11.4
Perspective Projections 189
Exercise 11.2
Draw various perspective views of a wire tetrahedron and a pyramid.
The only value required for the perspective transformation that has not yet been
discussed is PPD, the distance of the perspective plane from the eye. We can see
from figure 11.1 that different values of PPD produce pictures of different sizes.
Which one do we choose? Is there a correct value?
If we consider the practical situation, we note that the observer is sitting in
front of a television and the perspective view plane is identified with the plane of
the television screen . Normally the observer is sitting at a distance that is about
three times the height of the screen from the terminal. In the scale of our map-
ping from the real-world to the graphics area of pixels, this is a distance 3*VERT
(the value we used above). If we choose PPD greater than this value it is as
though we are creating a close up, and if PPD is less than 3 *VERT we get the
smaller image of a long shot.
Clipping
Theoretically, objects may be positioned throughout space, even behind the eye,
although we consider only points with positive a-coordinates in the OBSERVED
position. Even so some of these points go outside the cone of vision and become
invisible. In fact, part of the cone of vision is outside the screen area (we can,
after all, see the outside of the graphics area) . We are left with a subset of the
cone of vision, the pyramid of vision . Thus all'points outside this pyramid (that
is, those whose perspective transforms take them off the screen) must be ignored .
We noted that the Spectrum displays an error message whenever we try to DRAW
a line to a point off the graphics area. It is essential that we use the clipped
'lineto' routine (listing 3.4) in order to avoid any problems . In fact we further
limit scenes so that all vertices in the OBSERVED position will have positive z-
values; that is, all objects must lie in front of the eye (although not necessarily
inside the cone of vision) . This will avoid peculiar perspective projections of
points that lie behind the eye appearing to be on the screen.
Exercise 11.3
Experiment with perspective views of all types of wire figures; for example,
bodies of revol.ution, regular solids . Consider cases where an object is drawn in-
side the construction routine ; that is, the values of V and W must now be calcu-
lated here and not in the 'drawit' routine. Change the program that drew the jet
of figure 9.3 so that you get a perspective view; note that the farther the eye gets
190 Advanced Graphics with the Sinclair ZX Spectrum
from the plane the smaller it appears, a phenomenon that does not occur with
the orthographic projection.
Exercise 11.4
Write a hidden line algorithm for a single convex body, similar to that given in
chapter 10.
Note that since a convex facet is projected into a convex polygonal area in the
view plane, all we need do is calculate the coordinates of the vertices of the
projected facet , and hence find whether the facet is in anti-clockwise (in which
case we draw it) or clockwise order (in which case it is ignored).
Exercise 11.5
Write a program that draws a perspective view of a mathematical surface , similar
to that given in chapter 10. The method will be exactly equivalent to listing 10.4 ,
with the exception that you must work with the V/W values rather than the
X/Yarrays.
These hidden surface and line algorithms are perfectly adequate for specially
defined single objects, but we must now consider the more general case where a
number of objects are scattered about space.
Complete Programs
I. 'lib 1" 'lib3' and listings 9.4 ('cube'), 11.1 ('scene3') and 11.2 ('drawit'). Data
required: HORlZ, VERT, and repeated values for (EX, EY, EZ) and
(DX, DY, DZ). Try 9,6, (5 ,15 ,10) and (0, 0, 0) ; (1 ,2,20) and (0, 0,1).
12 A General-purpose Hidden Line
Algorithm
visible facets ; that is, those that , when projected, keep their anti-clockwise
orientation.
Let us assume that a typical line I'3 in the OBSERVED position joins the two
points (x I" Y I" Z I') and (X2:' Y2', Z2 '), and so a general point on this line is
Wesuppose that these two end points are projected on to the two points (x I' Y I)
and (X2' h) in the perspective view plane. Thus r 3 is projected on to this plane
as a line r 2 with general point
Note that the point (1 - 1/» (x I" Y I" Z 1 ') + I/>(X2', Y 2" Z 2') does not necessarily
transform into the point (1 - 1/» (Xl> yd + I/>(X2' Y2) : that is, I/> need not equal J.L.
Welet a typical facet il 3 be projected on to an area il 2 in the view plane and
assume that the H vertices on this projected facet are (Xt, Yt), where 1 .,;; i .,;; H
Let the i t h edge of il2 intersect r 2 at (1 - At) (Xt,Yt) + At(Xi+l.Yt+d. If
At < a or At > 1 then r 2 intersects the extended i th edge at a point outside the
area il2 : if a ~ At";; 1 then r 2 crosses the area il2 at a point on the i t h edge.
Since the projected projection of a convex facet is convex, then the number of
crossing points is either zero (and there is no point of intersection) or two
(perhaps coincident). We need consider only the case of two non-coincident
points: suppose their J.L values on I'2 are J.Lmin and J.Lmax where J.Lmin < J.Lmax . So
the points of intersection on r 2 are (1 - J.Lmin)(XI, yd + J.Lmin(X2, h) and
(1 - J.Lmax)(XI> yd + J.Lmax(X2' Y2) '
It is now necessary to discover whether the subsegment of I'2 between these
two points is visible or not. This is checked by finding the mid-point of the
segment (Xmid' Ymid) = (1 - J.Lmid) (XI, yd + J.Lmid(.X2; Y2), where J.Lmid =
CJ.Lmid + J.Lmax)/2. We then find the point (x, y, Z) on r 3 that has (Xmid, Ymid)
as its perspective projection. The segment of line between the points with J.L
values J.Lmin and J.Lmax is hidden if and only if (x, y, z) and the eye are on opposite
sides of the infinite plane containing ll3 . The equation of the plane is found
using the methods described in chapter 7, and the functional representation of
the plane is used to check the above requirement.
Note that x*PPD/z = Xmid, y*PPD/i =Ymid, and (x, y, z) lies on r 3 • So for
some value of I/>
Hence
A General-purpose Hidden Line Algorithm 193
and
that is
Ymid*ZI' - YI'*PPD
=
(Y2' - YI')*PPD - Ymid*(Z2' - Zl')
This enables us to calculate tfJ, and hence (x, y, z), which in tum is used to find
whether the subsegment r 2 is visible or not.
The algorithm given in routine 'hidden', listing 12.1, compares each line on
the objects with all the visible (anti-clockwise) facets. Note that all objects are
considered solid; that is, no individual planar facet occurs in such a way that it is
possible to see its underside (clockwise orientation). The lines are implicitly
stored in the facet data , and each line occurs twice, once from vertex IVI to
vertex IV2 (say) and once from vertex IV2 to IVI. Rather than duplicate effort
we consider only the case when IVI < IV2. Furthermore we compare the lines
with visible facets only, for if a line is partially obscured by a hidden facet (one
with clockwise orientation) then the invisible part of that line must also be
obscured by anti-clockwise facets.
Let us assume that at the time of comparing the line I'2 , which joins vertices
IVI to IV2 with the Kth facet, we have calculated NRL visible subsegments of
the line : NRL is assumed to be less than 50. The Il values for the end points of
the Mth visible segment are stored in array L at L(1,M) and L(2,M). Initially
NRL = 1, L(1, 1) = 0 and L(2, 1) = 1; that is, the complete line is assumed to be
visible. If at this stage a new hidden segment is discovered, specified by the values
Ilmin and Ilmax (the variables MIN and MAX), then the values in the L array and
NRL have to be adjusted accordingly.
When the line has been compared with all visible facets we are left with the
NRL visible segments that can then be drawn on the screen. If at any time NRL
becomes zero then the line is totally obscured and there is no need to continue
with comparisons with other facets.
Example 12.1
We can now draw a hidden line , perspective view of the scene we first saw in
figure 9.2: one of the two cubes shown in figure 12.1. The scene has HORIZ = 9,
VERT =6 and is viewed from (15,10,5) to (0, 0, 0).
194 Advanced Graphics with the Sinclair ZX Spectrum
Listing 12.1
8040 NEXT K
8049 Rl~ craw visibLe parts of Line ( if any).
8050 =
FOR K 1 TO NRL
8060 LET R1 = L(1,K): LET R2 = 1 - 1'1
8070 LET XP1 = X1*R2 + X2*R1
8080 LET YP1 = Y1~R2 + Y2*R1
8090 LET R1 = L(2,K): LET R2 = 1 - R1
810~ LET XP2 = X1*R2 + X2*R1
8110 LET YP2 = Y1*R2 + Y2*R1
8120 IF (ABS (XP1-XP2) < EPS) AND (ABS (YP1-YP2) < EPS) THEN GO TO 8150
8130 LET XPT = XP1: LET YPT = YP1: GO SUB moveto
8140 LET XPT = XP2: LET YPT = YP2: GO SUB Lineto
8150 NEXT K
8160 LET IV1 = IV2: LET X1 = X2: LET Y1 = Y2
8170 NEXT J
8180 NEXT 1
8190 RETURN
r-l
,' ------
........... /".--.-.....1-. _
e
Figure 12.1
We use 'lib 1" 'lib3' and 'hidden' (listing 12.1) together with the 'scene3' and
'cube' routines given in listing 12.2. This last version of 'cube' means that we
have considered all the array methods of constructing an object; that is, stored/
not stored, lines/facets . We deliberately used the cube over and over again in our
diagrams because it is such a simple object and it is easy to understand its various
constructions, and therefore it does not complicate our discussion of the general
principles of three-dimensional graphics. Now is the time to introduce complexity
into our objects: provided that you understand the limitations of the algorithms
then the ideas we have discussed will be equally valid. Users with the 16 K
Spectrum will find that programs of this complexity will not fit into their
machines. Such programs must be broken into independent sections and object
data and arrays must be stored temporarily on tape.
A General-purpose Hidden Line Algorithm 197
Listing 12.2
6000 REM scene3/figure 12.1
6010 DIM X(16): DIM Y(16): DIM Z(16)
6020 DIM V(16): DIM W(16): DIM F(4,12): DIM H(12)
6030 DIM A(4,4): DIM B(4,4): DIM R(4,4): DIM Q(4,4)
6040 LET cube = 6500: LET hidden = 7000
6050 LET FPD = 3*VERT: LET NOV = 0: LET NOF = 0
6059 REM put first cube in OBSERVED position: ( ACTUAL=SETUP).
6060 GO SUB idR3: GO SUB Look3
607E GO SUB cube
6079 REM copy ACTUAL to OBSERVED matrix i nt o Q.
6080 FOR I = 1 TO 4: FOR J = 1 TO 4
6090 LET Q(I,J) = R(I,J)
6100 NEXT J: NEXT I: GO SUB idR3
6109 REM caLcuLate SETUP to ACTUAL matrix.
6110 LET TX = 3: LET TY = 1.5: LET TZ = 2: GO SUB trar.3: GO SUB muLt3
6119 REM recover ACTUAL to OBSERVED matrix.
6120 FOR I = 1 TO 4: FOR J = 1 TO 4
6130 LET A(I,J) = Q(I,J)
6140 NEXT J: NEXT I
6149 REM caLcuLate SETUP to OBSERVED matrix.
6150 GO SUB muLt3
6159 REM pLace second cube and draw hidden Line view of scene.
6160 GO SUB cube
6170 GO SUB hidden
6180 RETURN
Listing 12.3
Listing 12.4
Exercise 12.1
Construct hidden line scenes composed of cubes, tetrahed ra, pyramids, octa-
hedra , cubo ctahedra, icosahedra . To help you , listings 12.3 and 12.4 give con-
struction routines for a cub octahedron and icosahedron. Write your own routines
for an octahedron, and perhaps even more complicated objects like the rhombic
dodecahedron (see Coxete r, 1974).
Listing 12.5
6 000 REM scene3/ t wo s ta rs hidde n line s removed
60 10 DI M X(22): DI M Y( ZZ): DIM Z(Z2 )
602 0 DI M V( 2Z) : DI M W( Z2 ): DIM F(3,3 6 ' : DIM H( 36 )
6 03 0 DI M A( 4,4) : DI M B( 4, 4 ): DIr. R(4 ,4) : DIM Q( 4,4 )
6 0 4111 LET s tar1 = =
65 111 0: LET s t a r ? 67 1110 : LET hidden =
7 0 00
6050 LET PPD =3*VERT: LET NOV = 111: LET NOF = 0
6059 RE M p Lac e f i rst st ar .
60 60 GO SUB i d R3 : EC' SUfi Look3
6070 LET A = 6: GO SUB s t a r 1
6080 FO R I = 1 TO 4 : FOR J = 1 TO 4
60 9111 LET Q(I, J) = R(I,J )
6 100 NEXT J: NEXT I: GO SUB i d R3
6 109 REM p La ce sec ond st a r.
6 110 LET TX = 5 : LET TV = 5 : LET TZ 5 : GO SUB tran3 : GO SUB mult3
61 20 FOR I ~ 1 TO 4 : FOR J = 1 TO 4
613 0 LET A(I,J ) = Q(I , J)
614111 NEXT J : NEXT I
61 50 GO SUB muL t3
6 160 LET A = 4: G0 SUB s ta r2
6170 GO SUB hi d de n
6180 RETUF.N
Listing 12.6
6500 RE~1 s t a r 1
65 0 1 REM I N and OUT : same as cu be a bov e.
65111 9 REM s t ar based on a c ub e .
65 H l DA TA 1,1,1,1,1,-1,1,- 1,-1,1,-1,1, - 1,1,1, - 1 ,1,-1, - 1,-1,-1, - 1,-1 , 1,
A, 0,11I, -A,0 ,0 , I1I , A, 0 , 0 , -A ,0 , 0,I1I, A, 0,11I,-A
6 52 0 DATA 1 ,2 ,9 , 2 ,3 ,9 , 3 ,4 ,9 , 4 , 1 ,9, 6 ,5 ,10 , 5, 8 ,1 0, 8 ,7 ,1111 , 7,6 ,1 0 ,
2 ,1 , 1 1 , 1 , 5 ,1 1 , 5,6,11, 6 ,2 , 11 , 4,3 , 1 2, 3,7,1 2, 7 , 8 ,12 , 8 , 4 ,12 ,
1,4,1 3,4, 8,13, 8 , 5 , 13 , 5 ,1 , 13 , 3, 2,1 4, Z,6 , 14 , 6 ,7 ,14 ,7 , 3 ,1 4
6 530 RESTORE s t a r 1
6 540 LET NV = "JQV
6550 FOR I = 1 TO 1 4
6 560 R E ~. D XX, YY, ZZ: LET NOV NOV + 1
65 70 LET X(NOV) XX* R(1 , 1) + YY *R( 1 , Z) + ZZ*R( 1 ,3) + R( 1 ,4)
6 580 LET Y(NOV) XX*R(Z,1) + YY *R(Z , 2) + ZZ*R(2 ,3) + R(2 ,4)
65 90 LET Z( NOV) XX* R( 3 , 1 ) + YY*R (3 , 2) + ZZ*R(3 ,3) + R( 3 , 4 )
6 6 0 0 LET V( NOV) PPD*X( NOV )/Z(NOV )
6610 LET W(NOV) =PPD*Y( NOV) / Z(NOV)
66 2111 NEXT I
6630 FOR I =1 TO 24
6640 REM F1, F2,F 3: LET NOF = NOF + 1
6650 LET H(NOF) =3
6660 LET F(1,NOF) F1 + NV : LET F<Z,NOF ) F2 + NV: LET F<3,NOF) F3 + NV
667 0 NEXT I
66 80 RETURN
200 Advanced Graphics with the Sinclair ZX Spectrum
Listing 12. 7
671i'H'1 REM sta r2
6701 REM IN ~n d OUT : same as cube above.
6709 REM star based on a tetrahed ron.
6710 DATA 1,1,1, 1,-1,-1, -1,1,-1, -1,-1,1, -A,-A,-A, -A,A,A, A,-A,A, A,A,-A
6720 DATA 2,1,8, 3,2,8, 1,3,8, 1,Z,7, 4,1,7, 2,4,7,
2,3,5, 4,2,5, 3,4,5, 3,1,6, 4,3,6, 1,4,6
6730 RESTORE st a r2
6741lJ LET NV = NOV
6750 FOR 1 = 1 TO 8
6760 READ XX,YY,ZZ: LET NOV = NOV + 1
6770 LET X(NOV) = XX*R(1,1) + YY *R(1, 2) + ZZ*R(1,3) + R(1,4)
6780 LET Y(NOV ) = XX*R(2,1) + YY*R(2,2) + ZZ*R(2,3) + R(2,4 )
6790 LET Z(NOV) = XX*R(3, 1) + YY*R(3,2) + ZZ*R(3,3) + R(3,4)
6800 LET V(NOV) = PPD*X(NOV )/Z(NOV)
6810 LET W(NOV) = PPD*Y(NOV)/Z(NOV)
6820 NE XT 1
6830 FOR 1 = 1 TO 12
6840 RE AD F1,F2,F3: LET NOF = NOF +
6850 LET H010 F) e: 3
6860 LET F(1,NOF) = F1 + NV: LET F(Z,NOF) F2 + NV: LET F<3,NOF) F3 + NV
6870 NEXT 1
688ft RETL'RN
Example 12.2
By now you will have realised that hidden line algorithms are very slow programs
- we have to make a large number of comparisons. It takes at least 5 minutes to
draw the two cubes of figure 12.1 . This means that we are rather limited in the
scope of objects we can draw. Nevertheless it is very good practice, and if you
have the opportunity to use larger machines you will see that the above algor-
ithm will work on these also, but much faster. We give a 'scene3' routine in
listing 12.5 and examples of two three-dimensional star-shaped objects in list-
ings 12.6 and 12.7 (both require a parameter A that changes the elongation of
the spikes). These two 'star' routines are based on the tetrahedron and cube.
Figure 12.2 was drawn with HORIZ = 48, VERT = 32, viewed from (35, 20 , 25)
towards (0 , 0, 0).
Figure 12.2
A General-purpose Hidden Line Algorithm 201
Exercise 12.2
The program in listing 10.1 checks that the order of the vertices of a triangular
facet are anti-clockwise . The program was devised for use with convex bodies
containing the origin . Extend it so that it can cope with the most general case;
that is, specify the position of the observer and the coordinates of a point inside
the object (not necessarily the origin) so that this point and the observer lie on
opposite sides of the infinite plane containing the facet. Use this program to
check the above star-shaped objects (in fact for these figures the origin could act
as the inside point) .
Then produce your own star-shaped objects based on an octahedron, cubocta-
hedron, icosahedron or dodecahedron . Always check the order of the vertices in
your facets. You can produce stars based on very simple bodies of revolution,
and we need not limit ourselves to symmetrical objects! For non-symmetrical
shapes you really need the extended version of program 10.1 . Provided that you
stay within the restrictions mentioned, then listing 12.1 will draw any shape.
Exercise 12.3
Add extra information about the objects you are setting up. As well as the ver-
tex and facet information, introduce another array L such that L(l ,I) and
L(2,I) hold the indices of the two facets that have the Ith line as their intersec-
tion, I ~ I ~ NOL. Then change the hidden line algorithm so that it ignores any
line that borders two invisible (clockwise) facets, and does not compare a facet
with lines that lie in that facet.
Now you have read (and understood) chapters 7 to 12 you will have found
that we have reached the limits of three -dimensional graphics on the Spectrum.
You must have access to larger computers if you wish to go further in your study
of this type of computer graphics . Moreover, you must study the techniques of
using data structures, as opposed to arrays, for setting up scenes. For example , a
complete scene can be regarded as a linked list of pointers, each of which refers
to a linked list of information about the facets on a particular type of object. The
facets themselves can be stored as lists of vertices! A seemingly complex idea, but
one that makes the context checking of such programs as hidden line algor-
ithms very much simpler. The relationships between objects and facets are
implicitly stored in the lists. When you have grasped these ideas you can go on
to the complicated graphics algorithms including methods for animating , colour-
ing and shading. We recommend books by Horowitz and Sahni (1976), and
Knuth (1972,1973,1981) for the study data structures, and by Newman and
Sproull (1973) for the really complex graphics methods. In the next chapter we
take a look at more advanced character graphics and introduce one method of
producing animated three-dimensional drawings.
202 Advanced Graphics with the Sinclair ZX Spectrum
Complete Programs
I. 'lib I " 'lib3', listings 12.1 ('hidden') and 12.2 ('scene3' and 'cube'). Data
required : HORIZ, VERT, (EX, EY, EZ), (DX, DY, DZ). Try (a) 9, 6,
(15,10,5), (0, 0, 0); (b) 9, 6, (-10,10, -10), (0,1,0).
II. 'lib1" 'lib3', listings 12.1 ('hidden') and 12.2 ('scene3'). MERGE listing 12.3
('icosa') and 12.4 ('cuboct') and change the 'scene3' routine as follows
Data required: HORIZ, VERT, (EX, EY, EZ), (DX, DY, DZ). Try (a) 9, 6,
(15,10,5), (0, 0, 0); (b) 9, 6, (-10,10,10), (0 ,1,0).
III. 'lib1', 'lib3' ,listings 12.1 ('hidden'), 12.5 ('scene3'), 12.6 ('starl') and 12.7
('star2'). Data required: HORIZ, VERT, (EX , EY, EZ), (DX, DY, DZ). Try
(a) 60,40, (10, 20, 30), (0, 0, 0); (b) 90 ,60 , (-10, 20, -10), (0, 1,0).
13 Advanced Programming Techniques
Control Codes
The INPUT command can be used to issue a prompt immediately prior to any
request for a data item. In most cases we just place the prompt, enclosed in
quotes , in the exact form we wish to be displayed, before the item in the INPUT
statement. On the Spectrum we can force the evaluation of any string expression,
including function calls, by placing brackets around the expression. In this way it
will be treated as though it were a prompt in quotes . This method is used simply
in the above listing but we can produce far more complex prompts. By building
204 Advanced Graphics with the SinclairZX Spectrum
Listing 13.1
Listing 13.2
Here CODE 6 is interpreted in its own right and not as a parameter following the
CODE for PAPER. A CODE 6 at the end of each line preceding a new section of
program can be used to force the display of a blank line in the listing. The word
REM can be hidden by placing CODEs for INK 0 (extended shift "0") after
REM and INK 7 (extended shift "7") before it.
As a rule it is best to write programs in modules . Highlighting the names of
the modules allows us to read , correct and adapt the programs with ease. To
demonstrate the improved legibility provided by these highlighting techniques
see figure 13.1, which shows part of listing 13.4 as it appears on the screen.
l o a d er fO I
maChine-COde ro utl n~
CLEAR 639.99
FOR -r=a TO :'L1 : .R E A D R
POKE? ~00TI ~ R : NEXT I
for
fe r r o u t u r-e
-
mClln o , oc ,a m
FOR - I =0 TO 2 -1-
:::::a-. TO 3 1. : PR INT AT I ~J
Figure 13.1
remaining sevenlines for these rows there are seven more pages of memory,
each page containing the data for subsequent lines. After these eight pages there
are another two sets of eight pages holding the line information for the rest of
the screen. From this we can calculate which bytes holds the data for the top
line of a character block, and know that each of the other lines down the block
is stored 256 bytes (one page) farther on. To work out the display-me position
of the top line of a character block we need to know in which third of the screen
(eight pages) the block lies. We need also to identify which of the 256 blocks in
this third we are considering. Wecalculate the position of the first line of a
character block in row R, column C by the following FuNction
The Z80 also has internal registers, in which numbers can be placed, and used in
the first lines of that particular third of the screen. The last two items establish
which of the 256 bytes along the page corresponds to the top line of the
character block.
Using another function with a character as the input parameter, we can find
the start of the data that define this character and then transfer these data to the
display-file locations
The above function uses the system variable CHARS (stored in locations
23606 and 23607, see chapter 5) to find the start of the table of character set
data. It then adds eight times the CODE for the required character and thus
finds the address of the first piece of data . Note that these two functions can be
used to write on the bottom two lines of the display, which are not normally
accessible, as is demonstrated by the program in listing 13.3.
Listing 13.3
100 REM main program
110 LET pri nt =
500
120 LET P$ = "9 FAKE statement, 120:1": LET ROW = 23: LET COL = 0
130 GO SUB pri nt
14l.!1 LET P$ = "print this anywhere on the screen": LET ROW = 7: LET COL = 16
150 GO SUB pri nt
160 PAUSE 250: STOP
If we try to move the display me of the Spectrum about rapidly, we soon flnd
that BASIC is not fast enough to transfer the required quantity of data adequate-
ly . The Spectrum is controlled by a Z80 microprocessor that is ideally suited to
the task of moving data about quickly. To program this directly (instead of
using BASIC as an intermediate language) involves an excursion into machine-
code. The Z80 machine-code has instructions, like the PEEK and POKE of
BASIC, which can be used to examine or replace numbers in various locations .
208 Advanced Graphics with the Sinclair ZX Spectrum
The Z80 also has internal registers, in which numbers can be placed , and used in
much the same way as variables in BASIC. So we can write a set of machine-code
instructions (that is, a program) to load one of these registers with a number from
a memory location, and then load the number from the register into another
memory location. This may not sound very interesting until you realise that the
Z80 chip can carry out this program about a million times in one second!
Unfortunately machine-code appears rather incomprehensible to a beginner,
looking like a stream of apparently unrelated numbers. It is more convenient to
use assembly language , which contains short words, mnemonic codes, that are a
great help in understanding the function of each machine-code instruction. The
Spectrum requires machine-code instructions to be stored in DATA statements
and then POKEd into the memory. It is helpful to include the assembly language
instructions as a REMark in the DATA line . Assemblers are programs that trans-
late assembly language into machine-code, but if an assembler is unavailable we
can use the table given in appendix A, page 183, of the Spectrum BASIC Hand-
book (Vickers, 1982) . One of the most powerful instructions available on the
Z80 chip is 237, 176 or, more comprehensibly, LDIR in assembly language . This
stands for LoaD and Increment Repeated, and is an instruction used to transfer
blocks of data from one place to another in the store. Before issuing this instruc-
tion , however, we must load some of the registers with various information. We
must load
For clarity we look at figure 13.2, an example machine-code routine, with the
equivalent Assembly language, and also a BASIC program, which performs
exactly the same function. Remember the Assembly language is just a way of
making machine-code more understandable. The complete program, listing 13.4,
POKEs the machine-code into the memory and then calls the routine. This
example copies the bottom third of the display me into the top third of the
screen.
Figure 13.2
Advanced Programming Techniques 209
Having POKEd the machine-code instructions into the memory we run the
routine by using the USR function with the start address of our routine. USR
with a numerical address simply calls the subroutine , in machine-code , starting
at that address. This call is executed in a BASIC program by a command like
LET A = USR 32000 (32000 is the address holding 17, the first byte of code).
Listing 13.4
10 REM Loader for machire-code rout ire
19 REM for 16K machires use cLear 31999.
20 CLEAR 63999
30 FOR 1=0 TO 11: READ A
39 REM for 16K use POKE 320~~.
40 POKE 64l1J~~ + I,A: NEXT I
Exercise 13.1
Type in the BASIC routine from figure 13.2 and time how long it takes to run .
Compare this with the time taken for the machine-code routine in listing 13.4.
You will find the machine-code is thousands of times faster.
We use a simple routine to transfer all the data necessary to display a picture
(both display me and attribute me) from somewhere else in the memory to the
screen memory locations. This provides a quick method of changing between
pictures. There is enough memory on the 48K Spectrum to hold five alternative
pictures or frames. We can use the program from listing 13.5 to construct five
machine-code routines at 30100,30200,30300,30400 and 30500 that will
transfer these pictures to the screen. Suppose five diagrams have been SAVEd on
tape. We LOAD them into the alternative frames in memory , from where they
can be copied on to the screen by calling the appropriate routine. This method
could be used to illustrate a lecture or a sales talk with a short slide show, switch-
ing almost instantaneously between slides. Naturally after five slides have been
shown then five more must be LOADed. This takes approximately 5 minutes
from tape but less than 10 seconds from disk.
210 Advanced Graphics with the Sinclair ZX Spectrum
Pressingone of the keys "1 " to "5 " when using the routine 'slideshow'
(listing 13.5) brings the required picture to the screen.
Listing 13.5
10~ REM loader for machine-code routines
110 CLEAR 29999: DIM F(5)
120 FOR I = 1 TO 5: RESTORE
130 LET F(I) =
120 + 27*(1 - 1)
14l;l FOR J = 0 TO 11
150 READ A: POKE 30~~~ + I*1~~ + J,A
160 NEXT J
170 NEXT I
This idea can be extended to produce 'movie's. For example, consider figure
13.3, which shows five frames produced by the three -dimensional routines of
chapter 11. These give views of the same spheroid are created using HORIZ =
3.2, VERT =2.2, NUMH = 10, NUMV =8 and PHI =0.4 *PI * IjNUMH, where
o~ I ~ 4. The 'movie' of this object , viewed from (1 ,2 ,3) to (0 , 0, 0) , can be
shown apparently rotating by repeatedly bringing the five frames on to the screen
in quick succession. This set of pictures is included on the tape and is shown
below.
After you have finished with the 'movie' or 'slideshow' program you should
type CLEAR 65367, for otherwise the next time you LOAD a program the
Spectrum will reply 'Out of Memory'.
Advanced Programming Techniques 211
(a) (b)
(c)
(d) (e)
Figure 13.3
212 Advanced Graphics with the Sinclair ZX Spectrum
Exercise 13.2
Draw a perspective view of a wire cube in frame 1, and the same view, but with
the hidden lines suppressed, in frame 2. Produce a movie consisting of these two
frames only. You will see the visible lines of the cube stay fixed but the hidden
lines will flash on and off. Construct a movie in which one of the 'star's from
chapter 12 appears to rotate .
Using machine-code for more complicated tasks requires a great deal more
thought and study . A good book on the subject and preferably one specifically
written for the Spectrum should be consulted (see Zaks, 1978 ; Hutty, 1981 ;
Woods, 1983). For those who wish to pursue more complex manipulations of
the screen in machine-code we give one more example. (Listing 13.6 scrolls the
graphics display area downwards. The fact that the display-file data are split into
lines of 32 bytes causes problems. To effectively move these data around we need
the start addresses of these lines. To calculate the addresses in a machine-code
routine requires a lot of programming effort and slows down the execution.
Instead we use a look-up table. For machine-code purposes an address is made
up of sixteen bits that are stored in two eight-bit locations, the lo-byte and the
hi-byte . Whenever we need to know the address of a line we simply look up its
two halves from the tables. We use BASIC to calculate the tables and to POKE
them into memory . These tables and the machine-code itself could be saved on
tape and reloaded if required . Listing 13.6 gives the machine-code program,
BASIC loader and table constructer. Listing 13.7 shows an equivalent BASIC
routine for the program. Note that the BASIC routine assumes the existence of
the same table used by the machine-code.
Exercise 13.3
Write a machine-code routine that moves the attribute file around the memory,
one row at a time. Then write a BASIC program that calls your routine each
time the graphics area has been scrolled down by eight lines.
Listing 13.6
1111 REM Loader for machine-code routine
2111 CLEAR 31999
3111 FOR I = 0 TO 74
4fJ READ A: POKE 32011l11l+I,A
50 NEXT I
The first item stored for each line is the line number (16 bits), which takes up
two locations and is stored as the hi-byte followed by the lo-byte. In all other
places values are stored in the standard lo-hi format. To illustrate suppose we
enter the line 10 REM. If we type
Listing 13. 7
we shall see that 0, 10 is stored in these locations. If we remove line 10 and enter
another numbered line at the start of the program, we see that the representation
of this new number is now stored in 23755 and 23756. The next two bytes give
the length of the present line (this can be used to find the start of the next pro-
gram line without traversing the whole of the present line). Now comes the
actual text of the BASIC line, each character or keyword taking up one memory
location. The end of the line is marked by a CODE 13 (or ENTER). After any
number given in decimal notation and stored as characters there is a CODE 14
Advanced Programming Techniques 215
(or NUMBER: see page 183 of the Spectrum BASIC Handbook (Vickers, 1982))
followed by a binary translation of the number, taking five bytes . Integers are
stored in a simple way, as explained on page 163 of the Spectrum BASICHand-
book (Vickers, 1982). Using this information about the contents of the memory
we can write a program that lists programs, including itself, by examining the
memory. This type of listing program (listing 13.8) can be of great assistance when
formatting listings in which statements all start on a new line, or when control
CODES need to be displayed.
Listing 13.8
H'lfl REM s e Lf- List i ng program
110 LET I = 23755: CLS
120 LET LINE = 256*PEEK I + PEEK (I + 1): IF LINE > 9999.5 THEN STOP
130 PRINT" " ; LI NE;
140 LET I = I + 2: PRINT PAPER 5;PEEK I;",";PEEK ( I + 1);" " ;
150 LET LENGTH = PEEK I + 256*PEEK (I + 1)
160 LET NUM = =
0: LET I I + 1
170 FOR J = 1 TO LENGTH: LET I = I + 1
180 LET P = PEEK I: IF P < 32 AND NUM <= 0 THEN LET NUM = 1
1W IF P = 13 THEN PRINT PAPER 4;P: PRINT: GO TO 230
200 IF P = 14 THEN LET NUM = 6
210 IF NUM > 0 THEN PRINT PAPER 6;P;",";: GO TO 230
220 PRINT CHR$ P;
230 LET NUM = NUM - 1
240 NEXT J
250 LET I = I + 1: GO TO 1 20
In order to renumber lines we need to change the line numbers stored in the
memory. This is true as long as the lines do not get out of numerical sequence.
Unfortunately this does not take into account GO TO or GO SUB, etc ., com-
mands. We must search out all GO SUB, GO TO, RESTORE and RUN keywords
in the program and, if they are followed by a simple integer, check whether the
line number has been changed . If it has, then both the numeric characters of the
number and the integer representation of the number must be altered. To delete
a range of lines all we have to do is extend the length of the line before the un-
wanted range, so that it completely covers the unwanted area. The correct length
of the line is re-established on editing and re-entering this line. The remainder of
the program is moved down through the store to continue from where the line
now ends. Listing 13.9 gives two routines that perform these tasks; the renumber
routine is entered by RUN 9900 and the delete routine by RUN 9970.
Exercise 13.4
Write a routine that can search through the BASIC memory and find any specified
sequence of CODEs. Use the method shown in the above delete program to set
the system variable E PPC (which controls the edit cursor : see page 174 of the
Spectrum BASIC Handbook (Vickers, 1982)) to the number of the line where
the sequence is found.
216 Advanced Graphics with the Sinclair ZX Spectrum
Listing 13.9
990fJ INPUT " Renumber Li nes "; LOW;" To " ; UP, " St a r t i ng a t "; NEW;
" in steps of " ; STEP
9901 LET X = 990fJ: I F LOW > X OR UP > X OR LOW > UP THEN GO TO 990fJ
9902 IF NEW > X OR NEW < 1 OR STEP> X OR STEP < 1 THEN GO TO 990fJ
990 3 PRI NT AT 19 , 0;"Renumbe r Li nes ";LOW;" t o " ;U P,"Sta r t irg at " ; NEW ;
" i n ste ps of " ; STEP, " PLease wai t"
9904 L ET START = 23755 : LET SCREEN = 163 84: LET HI = 256
9905 LET A = START: LET B = SCREEN
9906 LET H = PEEK A: LET L = PEEK (A + 1)
9907 POKE B,H: POKE B + 1,L
9908 LET A = A + 2: LET B = B + 2
9909 LET L EN = PEEK A + PEEK ( A + 1) *H I: LET A = A + LEN + 2
9910 IF H*HI + L < X THEN GO TO 9906
9911 LET A = START : LET B = SCREEN: LET W = NEW
9912 LET H = PEEK A: LET L = PEE K (A + 1)
9913 LET N = HI*H + L: IF N < LOW OR N > UP THEN GO TO 991 8
9914 IF W > X THEN GO TO 9955
9915 LET H = INT (W/HI): LET L = W - H*HI
9916 POKE A,H: POKE A + 1,L
991 7 LET W = W + STEP
9918 LET A = A + 2
9919 LET LEN = PEEK A + PEE K (A + 1)*HI: LET A = A + LEN + 2
9920 IF N < X THEN GO TO 9912
9921 LET A = START: L ET B = SCREEN
99 22 LET H = PEE K A: LET L = PEEK ( A + 1): LET NN = PEEK B*HI + PEEK (B + 1)
9923 LET N = H*HI + L: IF N = X THEN STOP
992 4 PRINT AT 21,0 ;"Ch e c king " ; NN; " = new Line ";N
992 5 L ET A = A + 2 : LET W = A + 2
9926 LET LEN = PEEK A + PEEK (A + 1) *HI: L ET A = A + LEN + 2
9927 L ET T = PEE K W
99 28 IF T = CODE (" GO TO " ) OR T = COD E (" GO SUB " ) OR T = CODE ( " RUN ")
OR T = CODE (" RESTORE ") THEN GO SUB 99 3 2
9929 IF T = 14 THEN L ET W = W + 5
9930 LET W W + 1: IF W < A THEN GO TO 9927
9931 LET B B + 2 : GO TO 9922
993 2 L ET V W + 1: LET A$ =
9933 L ET S PEE K V: IF S <> 32 AND S <> 14 AND NOT (S >=4 8 AND S<=5 7 )
THEN RETURN
9934 IF S <> 14 THEN LET V = V + 1: LET A$ = A$ + CHR$ S: GO TC 993 3
993 5 LET V = V + 3 : LET AT = PEEK V + PEEK ( V + 1)*HI
993 6 IF AT < L OW OR AT > UP THEN RETURN
9937 GO SUB 9943 : LET H = IN T (NAT/HI): LET L = NAT - H*HI
99 3 8 LET B$ = STR$ NAT: IF LEN A$ <> LEN B$ THEN GOSUB 9951
9939 PO KE V,L : POKE V+1,H
99 40 FOR I = 1 TO LEN B$
9941 POKE W + I, CODE B$(I ): NEXT I
994 2 RETURN
9943 LET C START: LET 0= SCREEN
99 44 LET H = PEEK 0 : LET L = PEEK (0 + 1)
99 45 LET E = PEEK C *HI + PEEK (C + 1)
9946 IF E >= X THEN LET NAT = 0: RETURN
9947 IF H* HI + L = AT THEN LET NAT = E: RETURN
99 48 LET C = C + 2 : LET C = 0 + 2
9949 LET L EN = PEEK C + PEEK ( C + 1 ) *HI: LET C C + LEN + 2
9950 GO TO 994 4
99 51 LET 01 FF = LEN A$ - L EN B$
9952 IF DIFF > e THEN FOR I = 1 TO DIFF: LET B$ = B$ + " ": NEXT I: RETURN
9953 PRINT AT 16 , 0 ; "No r oom at " ; CHR$ T;AT;" i n Line '; ; NN,
"t ype edi t and add " ; - DI FF; " spa ce(s)","to LabeL th en re-run pr ogram."
9954 L ET H = I NT ( NN/ HI ) : POKE 23 6 26 , H: POKE 236 25,NN- H*HI
Advanced Programming Techniques 217
When programming in BASIC we can use our knowledge of the way lines are
stored to help make our programs more efficient, both in terms of space used
and speed of execution. Every occurrence of an explicit number is followed by
six bytes of number codes so that the conversion from a string of digits to its
binary form need not be performed each time the line is entered. If instead we
assign the required number to a variable, and use the variable name for each
occurrence of the value, then the space required would be only the number of
bytes in the variable name. To assign the value we must use three extra codes ":",
"LET" and "=", as well as the variable name, and to access the value we must use
the variable name. If the value is used often then this method will save consider-
able space. As an example consider the first two statements in the following
program
10 PRINT AT 1,1
20 LET A = 1: PRINT AT A,A
30 PRINT AT 1,1
40 PRINT AT A,A
218 Advanced Graphics with the SinclairZX Spectrum
The BASIC text of line 10 requires 17 bytes of store: "PRINT", " AT", "1",
14,0,0,1 ,0,0, ", ", "1",14 ,0,0,1,0,0.
Line 20 requires 16 bytes of store : " LET", "A", "=", "1", 14,0,0, 1,0,0,
":", "PRINT", "AT", "A", ",", "A" .
The second line above has fewer codes than the first line. However, the
second line uses the variable A, which, unless it is used elsewhere, will take up
six bytes of memory . If we need to repeat the statements, as shown above in lines
30 and 40, we find that line 30 is 17 bytes long but line 40 is only 5 bytes long.
So if we use a numeric value more than a couple of times in a program , we can
make large savings by using a variable to represent the value.
We can also save space and improve speed by packing as many statements as
possible on to one line, although this reduces legibility . Each new line requires
five bytes of memory: one for the CODE 13 at the end of the previous line, two
for the new line number and two for the length of the line. A colon between
statements needs only one byte . The space saved can be quite large. For example,
consider a program with 90 statements (quite small on average): if the state-
ments are initially all on separate lines and we rewrite with 3 statements per line,
we can save 60*(5 - 1) = 240 bytes . In a large program it is quite feasible to save
over 1K of memory in this way. Using fewer lines also makes it easier for the
machine to obey GO TO or GO SUB commands, as it will take less time to search
for the destination line. To be most efficient all SUBroutines and FuNctions
should be near the start of a program and arranged so that the most frequently
used come first. Unless very many calls are made to the same routine, it is usually
more convenient to store the routines in a logical order.
Exercise13.5
Rewrite and reposition the graphics routines and/or the games programs to use
less space and if possible run faster.
Synchronous Display
When displaying pictures on the Spectrum we can use the PAUSE command to
provide an interesting BORDER display. The television display is completely re-
drawn every 1/50th of a second (l/60th in the U.S.A.) and PAUSE uses multiples
of this same time interval. When PAUSE I is used the delay is not always a com-
plete 50th of a second. In fact the PAUSE will last only until the start of the
next frame. This gives us a method of starting instructions at a fixed time relative
to each refresh of the display. If we change the BORDER colour partway
through the drawing of the display, then for that frame the top part only of the
BORDER will be one colour and the remainder will be another colour. Obviously
we could use PAUSE to wait for the start of the next frame and repeat the pro-
cess. This results in the display of a steady picture provided we keep repeating
the same actions. The SAVE and LOAD statements use a machine-code routine
Advanced Programming Techniques 219
1 GO TO 10
2 PAUSE 1: BORDER 7: BORDER 2 : BORDER 6: BORDER 4:
BORDER 5 : BORDER 1 : BORDER 3: BORDER 7: GO TO 2
Exercise 13.6
Write a one-line program that alternates between red and cyan BORDER colours
without using PAUSE. Insert extra colons (no other statements - just colons)
between the statements until the execution time for the line is exactly 1/50th
(or 1/60th) of a second; that is, the two colours are stationary. (Hint: count a
BORDER command as 10, a GO TO as 13 and a colon as 1; try to make the total
value of the line, counted in this way, about 160.)
Complete Programs
I. Listing 13.1 . Data required : 10 sets of X/Y coordinate pairs. The screen
shows a zeroed table of these values, followed by "End." . A cursor , which
points to the first row, can be moved down by Capital "6" and up by
Capital "7 ". When you are at the required row, type "EDIT" (Capital
"1 ") and the machine requests the pixel X/Y coordinates for that entry in
the table . When you have finished move the cursor to "End.", then EDIT
and type " y"(es). The program will then draw a polygon by joining the
10 coordinate points.
II. Listing 13.2. Data required: numeric key input for mythical me system.
Type" 1" to "6". For "5" or "6" the machine checks if you really mean it :
type "y"(es) or "n"(o). BREAK terminates program.
III . Listing 13.3. No data required: BREAK terminates program.
IV. Listing 13.4 . Type any key on request. No data required: BREAK termin-
ates .program .
V. Listing 13.5. After the BEEP, program requires five pictures to be loaded
from tape . Type "1 ", "2", "3", "4" or "5" for instant display of required
frame, 'm' for 'movie ' and's' to stop movie.
VI. Listing 13.6. When screen is full, hold down any key for scroll to continue .
BREAK to stop .
220 Advanced Graphics with the SinclairZX Spectrum
RUN 9900
Renumber lines 10 to 30
Starting at 60 in steps of 5
RUN 9970
Delete lines 60 to 99
RUN 9970
Delete lines 65 to 99
EDIT (Capital "1")
LIST
14 A Worked Example of a Video
Game
On the screen we draw a scene of a green island with a small hill, dark blue sea,
and a light blue sky containing a yellow sun and white cloud. There are three
trees , and a light blue concrete area on which a tent is drawn. A man comes out
of the tent and walks to a sandbagged gun emplacement, where he gives 20
bullets to the gunner and returns to the tent. Enemy aeroplanes appear out of
the sun , fly over the hill and bomb the camp ; the gunner defends the camp by
firing at the aeroplanes. At every attack, the aeroplane is either disabled or makes
a successful hit on the camp. With each successful hit the size of the tent dimin-
ishes. After 14 hits the tent disappears, and the next plane bombs the gun. On
destruction of the gun the BORDER of the screen goes haywire and the scene is
reset. After each bombing run, the plane flies through the cloud, over the gun
and exits stage left. The stock of ammunition is replenished after 20 planes have
completed their runs. After the gun has been bombed for the third time the
screen goes blood red and the whole game starts again.
The gun has seven firing positions specified by keys" 1" to " 7", and a missile
is fired by pressing "x". Because of speed restrictions only one plane and one
missile can appear on the screen at any given time.
For you to make the most of the explanations in this chapt er we advise you
to get the companion cassette tape , and LOAD and RUN listing 14.1. The pro-
222 Advanced Graphics with the Sinclair ZX Spectrum
Listing 14.1
469 REM check whether you beat the HiSCore then restart game.
470 IF SC > HSC THEN GO SUB hiscore
480 PAUSE 200: GO TO 310
1 5011l REM r e l oa d
150 9 REM if th e camp is de s t r oy e d t he n don t re loa d a mmun i t i o n
1510 IF HIT THEN RETURN
1519 REM e xplic it i n s t r u c t ion s f or a nimat i on o f f i g u r e .
1 520 BRIGHT 0: LET R = 1 8
15 2 9 REM wal k fr om tent to trees.
1 530 FOR C = 24 TO 19 STEP -1: PR INT AT R,Ci"S ": PRINT AT R+1,C i"S"
: BEEP 0 .0 1,-15: PAUSE 5
1 5 40 PRI NT AT R,C;"R ": PR I NT AT R+1,Ci"r ": PAUSE 3
1 550 PRINT AT R,C;" ": PRI NT AT R+1,C;" ": NEXT C
1559 REM u s e ov er- p rint i ng s o tha t trees aren't da mage d as figure get s clo s e.
1560 OVER 1: PRINT AT R,18;"S": PRINT AT R+1,18; "s": BEEP 0.01,-15: PAUSE 5
: PRINT AT R,18;"S": PRINT AT R+1,18;"s"
157 11l PRINT AT R,18i" R": PRINT AT R+1,18i"r ": PAUSE 3
: PRI NT AT R,18i"R ": PRIN T AT R+1,18i"r "
1579 REM make noi ses as t h o ugh fi gur e goe s behind t r e e .
15 80 FOR J = 1 TO 2: BEEP 0.0 1,-15: PAUSE 11: NEXT J
15 89 REM s ho w feet e mer gin g from be h i nd tree.
1590 PRINT AT R+1,15;"s": BEEP 0.01,-15 : PAUSE 5 : PRINT AT R+1,15;"s "
16011l PRINT AT R+1,15i"r": PAUSE 3: PRIN T AT R+1,15i"r "
16 09 REM s how whole fi g ure co mi ng o ut o f tree.
1612) PRINT AT R,14i"S" : PRINT AT R+1,14i"S": BEEP 0.01,-15: PAUSE 5
: PRINT AT R,14i"S": PRINT AT R+1,14i"S"
1620 PRINT AT R,14i"R": PRINT AT R+1,14i"r": PAUSE 3
: PRINT AT R,14i"R": PRINT AT R+1,14i"r"
A Worked Example ofa Video Game 225
1629 REM wal k from tree to end of str ip, onto grass at column 8 with bright on.
1630 OVER 0: FOR C = 1 3 TO 8 STEP -1: BRIG HT CC = 8)
1640 PRINT AT R,Ci"S ": PRINT AT R+1,Ci"S": BEEP 0.01,-15: PAUSE 5
1650 PR INT AT R,Ci "R": PRINT AT R+1,Ci"r": PAUSE 3
16 6 0 PRI NT AT R,Ci" " : PRINT AT R+1,Ci " " : NEXT C
166 9 REM pr i nt fig u r e a t co l umn 7 r eady t o refil l amm o dump.
1670 PRINT AT R, Ci " R" : P RI NT AT R+1,Ci "r"
1679 REM r efil l ammo d ump, wi th s o un d ef f e ct s.
16 80 FOR G = 6 TO 5 STEP - 1: FOR H = 18 TO 17 STEP - 1: FOR N 2 TO 9
16911l PR IN T AT H,G i INK 3 i N$ CN) : BEEP 0.0 2,H+G -N
1700 NEXT N: NEXT H: NEXT G
1709 REM remove figure f r om co lumn 7 .
1710 PRINT AT R, Ci " ": PRINT AT R+1,Ci" "
17 19 REM r e s et ammun it i on pr int and truth flags.
1720 LET N = 8: LET OUT = 0: L ET H = 17: LET G = 6
1729 REM wal k from e n d o f s t ri p to tr ee, br ight off after column 8.
17 30 FOR C = 8 TO 13 : BRIGHT CC = 8)
1740 PRINT AT R,Ci")": PRINT AT R+1,Ci"+": BEEP 0.01,-15: PAUSE 5
1750 PRINT AT R,Ci "C" : PRINT AT R+1,Ci"*": PAUSE 3
1760 PRINT AT R,C i" ": PRINT AT R+1,Ci" ": NEXT C
1769 REM o v er- p r i nt as f i gu re r ea ch e s tree.
1770 OVER 1: PRIN T AT R,14i")": PRINT AT R+1,14i"+": BEEP 0.01,-15: PAUSE 5
: PRINT AT R,14i" )": PRINT AT R+1,14i "+ "
17 80 PR INT AT R,14i"C": PRINT AT R+1,14i"* ": PAUSE 3
: PRINT AT R,14 i"C ": PRINT AT R+1,14i"* "
1789 REM ove r p r int legs as t hey go int o t ree.
1790 PR INT AT R+1,15i" +": BEEP 0.01,-15 : PAUSE 5: PRINT AT R+1,15i"+"
1 800 PRINT AT R+1,15 i"*": PAUSE 3 : PRINT AT R+1,1 5i "*"
1 809 REM make no ises a s th ough f igur e i s be h i nd tree.
1 810 FOR J = 1 TO 2: BEEP 0.01,-15: PAUSE 11: NEXT J
1 819 REM ove rp rint as fi gu re emerge s f r o m tree.
1820 PRI NT AT R,1 8; ")": PRIN T AT R+1,18;"+": BEEP 0.01,-15: PAUSE 5
: PR I NT AT R,18i" )": P RIN T AT R+1,18i"+"
1 83 0 PRINT AT R,18; " C": PR INT AT R+1,1 8i"* ": PAUSE 3
: PRINT AT R,18i " C": PR IN T AT R+1 ,18i " * "
183 9 REM wa l k fro m t ree to te nt.
1 84 0 OVER 0 : FOR C = 19 TO 24: PR INT AT R, Ci") ": PRINT AT R+1,C i"+"
: BEEP 0.0 1,-1 5: PAUSE 5
1 850 PRINT AT R,Ci"C ": PRINT AT R+1,Ci "*": PAUSE 3
1860 PRINT AT R,Ci" " : PRINT AT R+1,Ci" " : NEXT C
186 9 REM r eset Row a nd Co lumn f or u se by plane rout ine.
1 870 LET C = =
0: LET R 2 : B RIGHT 1
1880 RETURN
2098 REM if missile is on same column as plane check for being near row of plane.
2099 REM if missile is close enough explode and reset missile to ready.
2100 IF X C THEN IF ABS (Y - R) < 2 THEN GO SUB explode: LET m = missile
: GO TO m
2109 REM if missile is off screen then reset pointer 'm' to ready.
2110 IF X < 0 OR Y < 0 THEN LET m = missile: GO TO m
2119 REM print missile at new position.
2120 PRINT AT Y,X;M$: RETURN
2200 REM missile/ready to fire
2210 LET X = 3: LET Y = 16
2220 RETURN
2500 REM explode
2508 REM missile has hit plane so blow up plane and add to score.
2509 REM check whether explosion has taken place in cloud or in sky.
2510 OVER 0: LET SKY = (R <> 3 OR C < 16) .
: IF NOT SKY THEN PRINT AT R,C-1;"
2519 REM cent ral flash and low buzz, add to score for hitting plane.
2520 INK 2: IF SKY THEN PRINT AT R,C;"t"
2530 FOR 1=1 TO 5: BEEP 0.01,1 - 10: BEEP 0.01,-1 - 10: NEXT I: LET SC SC + ,
2539 REM if explosion is seen then print cloud of debr is.
254lil IF SKY TH EN PR INT AT R-1, C-1 ; ">@<": PR INT AT R,C-1 ; "$! &"
: PRINT AT R+1 ,C-1 ;":%;"
2549 REM high pitched whizz.
2550 FOR I = -4 TO 4: BEEP 0.002,(50 + ASS I): NEXT I
2559 REM remove all trace of explosion.
2560 INK 0: IF SKY THEN PRINT AT R-1 ,C-1;" ": PRINT AT R,C-1;"
: PRINT AT R+1 ,C-1;" "
2569 REM print out new score l ine, reset plane and missile pointers.
2570 INK 8: GO SUB status: LET m = missile: LET p = plane
2580 LET R = 2: LET C = 0
2590 RETURN
gram will ask you to LOAD first the special character set , 'gameset' (for drawing
the plane, tent, man, etc.), and then the 'background' (which sets the scene),
before starting the action . The listing is well documented, so there is no need
for us to go into too much detail here. We shall simply outline a general approach
for constructing these games, and describe methods of solving typical problems
that arise.
The Foreground
It is essential to carefully plan your game before you start writing the program.
You must first draw a rough plan of the proposed game scene on graph paper .
Then sketch in the positions of fixed objects (for example, the sun and clouds)
and also the areas to be traversed by moving objects (for example, the path of
character blocks that at some time will contain characters to make up the planes).
This should fIX the scale of the objects to be used, and will give a general impres-
sion of the final game. Time spent in careful planning at this stage will be a
fraction of that needed to adjust a nearly completed program . You must ensure
that the proposed display can actually fit into the graphics area, and that you
never get a situation where a moving object introduces a third colour within a
character block. Once you have decided on a screen layout, you must then
create the objects for the foreground (for example, explosions , the plane , the
man) - that is, the moving parts. The fastest BASIC command for putting a
large object on the screen is the PRINT statement. So we build our objects out
of defmed character blocks, and use the CHARACTER GENERATOR program
of chapter 5 to produce the required shapes. To get exactly the shape you want
for a display, you should repeatedly examine the characters at their normal size
and re-edit them if you are not satisfied . It may be helpful to add parts of your
game-program at the end of the CHARACTER GENERATOR program (option
7, see chapter 5). This will allow you to observe these objects in action while
their final form is still under consideration.
Exercise 14.1
Add an extra option to the CHARACTER GENERATOR program so that you
can edit a single character from within a group. This option should POKE the
eight BINary values of the character grid being edited, together with the other
characters in the group , into the display-file locations for specified positions on
the screen; for example, the two blocks that make up the plane in horizontal
flight to the right (see figure 14.1). This quickly allows you to evaluate the
merits of altering one pixel within a character.
A Worked Example ofa Video Game 229
Figure 14.1
The Background
(1) The planes pass over and behind a cloud on their return run , so the cloud
contains a path of white blocks through its middle . Note that blue sky and white
cloud cannot occur within the same character block on the path of the plane, so
the boundary between sky and cloud on this path must be a straight edge. The
plane actually disappears behind the cloud during its flight, so some of the
blocks in this path contain white INK and white PAPER. Thus when the plane
230 Advanced Graphics with the Sinclair ZX Spectrum
enters this block the INK pixels take on the same white colour as the PAPER
and the plane is indistinguishable from the cloud.
(2) The sun, which appears to be a smooth circle, has flat edges on each side.
The planes pass through one of these edges without encountering a combination
of sun and sky within one block. This is shown in close-up in figure 14.1, which
was created with the aid of the 'big pixels ' routine of listing 5.2. This diagram
ignores the true attributes of the blocks; it colours INK in black and PAPER in
white. The true colours can be ascertained from the 1/16th scale picture drawn
in the top left-hand comer. Naturally those character blocks in the scene that
remain constant throughout the game can contain two colours .
Figure 14.2
Cascade Animation
will be undertaken on each separate entry to the routine. The solution to each
subtask is laid out as a set of lines, after which the pointer to that routine is
altered and we return to the calling program. On re-entry, the program naturally
obeys a different section of code within that routine , usually the pointer changes
to the next subtask down the cascade . With each call, this process continues with
the pointer moving down through the sections of the cascade until it eventually
reaches the bottom (where it will usually be reset to the top). The complete game
will consist of an initialisation phase followed by a loop of calls to routines, each
of which is a cascade that solves a specific problem within the game (for example ,
to move the aeroplane or to bomb the base). Cascade routines may even call
other cascades! This gives an impression of a parallel process, which is essential
if users are to believe that they are playing a game in which a number of different
events are happening simultaneously. In our game we try to give the impression
that the plane, gun and missiles can all move independently.
Our game is a little too complicated to be an elementary illustration of this
technique, so we introduce another, very much simpler, game (available on 16K
machines as well) . Consider the following two programs that perform indepen-
dent functions. Listing 14.2 waits until a key is pressed then shoots a dot across
the screen; listing 14.3 continuously moves a plus sign up the screen in a zig-zag
pattern. Both programs use the fast animation techniques found in the ISLAND
DEFENCE program ; however , because each is so small, it is necessary to slow
them down with a PAUSE command (note that excess speed is only a problem
when programs are very simple) . From these listings we create two cascade
routines (listing 14.4) so that the dot and cross appear to move simultaneously.
The dot cascade is in three parts : (a) place a dot at row 11, column 0; (b) if
the user hits the keyboard then start the dot moving; and (c) move the dot one
column farther across the screen until it hits the right-hand edge. Whenever one
of these processes is flnished the identifier of the routine 'dot' is moved down to
the next section. After the dot has moved right across the screen, 'dot' is reset
to the start position.
The cross cascade is also in three parts : (a) place the cross at row 22, column
8; (b) move the cross one row up and diagonally to the right on the bottom half
of the screen ; and (c) move one row up and diagonally to the left in the top
half, and if the dot and cross lie in the same block, then SPLAT.
The two routines are turned into a game by a main routine that loops repeat-
edly through calls to 'dot' and 'cross', where, of course, these identifiers are
perpetually changing so that they each refer to the correct part of their cascade.
Listing 14.2
20rl REM dot
210 PRINT AT 11,0;"."
220 IF INKEYS = "" THEN GO TO 220
230 LET 0 =0
2"" PRINT AT 11,0;': ":LET 0 =0 + 1
250 IF 0 = 32 THEN GO TO 210
260 PRINT AT 11,0;"."
270 PAUSE 1: GO TO 2""
232 Advanced Graphics with the Sinclair ZX Spectrum
Listing 14.3
3erl REM cross
311'1 LET R =21: LET C 8 =
321'1 PRINT AT R,C;"+"
331'1 PRINT AT R,C;" ":LET R = R - 1
3411 IF R < e THEN GO TO 311'1
350 IF R > 11 THEN LET C = C + 1
360 IF R <= 11 THEN LET C = C - 1
370 PRINT AT R,C;"+"
380 PAUSE 1: GO TO 330
Listing 14.4
10rl REM main loop
110 LET dot =
200: LET cross = 300
120 GO SUB dot: GO SUB cross
131'1 GO TO 120
380 IF R =
11 AND C =
0 THEN PRINT AT R,C;"SPLAT": STOP
390 PRINT AT R,C;" ":LET R R - 1 =
4110 IF R < 0 THEN LET cross = 310: RETURN
410 LET C =
C- 1
420 PRINT AT R, C; "+"
431'1 RETURN
Exercise 14.2
Add a line to the calling loop of the above program so that after a SPLAT the
pointers to the top of the cascades are reset and the score (number of hits)
printed.
Write a 'duck shoot' game where a hunter , under keyboard control, moves
left and right at the bottom of the screen . He shoots at ducks that fly left to right,
up and down, across the screen.
A Worked Example ofa Video Game 233
With such a simple game we find that the saving,in time and programming
effort, from using the cascade technique is very small. However in larger pro-
grams, which can have complicated cascade programs (for example , the move-
ment of the plane in ISLAND DEFENCE), the time savings can make the
difference between a good fast game and a boring slow one. Note that there are
eight different sets of character blocks that describe the plane; see figure 14.2,
which shows the complete scene and one example of each plane, as well as other
foreground objects. The cascade for drawing the plane is divided into sections,
each of which places one type of plane in a particular area of the screen.
In this game we show a variety of different ways of solving the problems of
animation. The planes are moved by printing them in transparent INK, and then
obliterated from their previous positions with blanks. The missile characters are
OVERprinted (again with transparent INK) on the existing detail and then
erased at their last position by OVERprinting with the same character. These
methods have both advantages and disadvantages. Where removal of the old
position can be combined with the print ing at the new (see the sections where
the plane flies horizontally) you will find that normal PRINting often takes less
time , but it does mean you lose any detail in the background . OVERprinting
takes longer, but leaves detail undamaged. Using a combination of these tech-
niques in a program can cause problems if two moving objects pass simultaneously
through the same block : for example , what happens if a square containing a
missile is blanked out by a passing plane? This can occur in the game at character
block row 7, column 9. The effect is that the OVERprinted missile, which is
supposed to cancel out the old one, is left hanging in mid-air. Discretion is the
better part of valour and it is far better (timewise) to check for these problems
and explicitly program a cover-up, rather than add complexity into your algor-
ithm and perhaps even introduce another fault. The plane cascade contains an
ext ra statement (at line 1190), which ensures that any such mishap is swiftly
covered. When trying to remove faults , remember that a brute-force cover-up
will probably be far quicker (in your time, and running time) than a fancy fault-
avoidance routine. Although you should have foreseen most problems in the
planning stage, and adjusted the background and the paths of moving objects ,
some peculiarities are certain to occur .
There are two places where we have shown an object apparently passing
behind a background object. In order that a plane can vanish behind the cloud,
we simply made the INK white in those blocks where the plane was to disappear.
Since a white plane in a white cloud is about as easy to spot as a black cat in a
coal-cellar at midnight , we are tricked into thinking that the plane is hidden by
the cloud. For the man marching behind the tree, the whole problem becomes
more complicated. We still wish to see the tree in two colours when he is behind
it . The trick this time is to make the man walk up to the tree normally ; OVER-
print him when he is in the same block as outer foliage, and then simply make
234 Advanced Graphics with the SinclairZX Spectrum
marching noises, without printing, for the appropriate length of time, before
starting him off again from the other side of the tree. His legs must also reappear
from behind the tree one block before his head when he is moving right, and
equivalently when moving left. All these details must be carefully calculated
before even writing the flrst line of BASIC code .
A combination of these techniques for moving objects, making an allowance
for them to pass each other, will enable you to produce displays of very high
quality . Of course you must use machine-code routines if you require really fast
and complex games. Even so, many of the routines can still be in BASIC. There
is no need to produce programs written completely in machine-code unless you
want to sell your games.
Finally, as your games programs become more and more interwoven and
cross-connected, you must keep a simple overview of the program . Note what
tasks need to be done at the top level, use sensible variable names , put in plenty
of comments during development, and above all, don't panic!
We leave you now with your Spectrum. It has proved reliable and sturdy,
straightforward and easy to use. We are certain that you will have many, many
hours (years!) of pleasure out of this machine . To start you off we give a number
of ideas for projects in the next section . Good luck and good programming.
Complete Programs
I. Use you r Spectrum to draw a digital clock. Use the special large characters for
the digits and a colon to separate them. Your clock can be made to keep correct
time by using the internal clock of the Spectrum (see page 130 of the Spectrum
BASIC Handbook (Vickers, 1982)).
II. Make a program that tests the Morse Code proficiency of the user. The con-
tent for the program should be a paragraph of text ; after translating into Morse,
the Spectrum should print out the dots and dashes using the medium-resolution
character blocks. It should also use BEEP to simulate the sound of Morse. Your
program should have a variable rate of production of the Morse Code, so that the
speed of the test can increase as the user becomes more proficient.
III. Draw a set of international road signs. Your program should draw the back-
ground of the figures (for example , red triangles); then use your own special
routines or the programs of chapters 5 and 6 to finish off the foreground.
IV. Construct crossword puzzles on your television set. Each square of the puzzle
should be 2 by 2 character blocks. The four blocks can be either black (in which
case nothing goes in the square) , or white , with the bottom left-hand corner hold-
ing the letter of a solution and the top two characters the clue number (if any) .
This allows space for a 16 by 11 puzzle.
As you have also to place the clues on the screen, there will obviously be a
shortage of space . This problem can be solved by using the ideas of the 'slide
show' of chapter 13; put the puzzle in one frame, and the clues on the remaining
four frames. Solutions to the puzzle can be added by a 'cursor' method or by
having a special input code ; for example, letter "A" (across) or "D" (down)
followed by the number of the clue, followed by your solution. If you make the
puzzle smaller you could even have the option of using this code to bring clues
on to the screen one at a time (or perhaps place them in rows 23 and 24), in
which case the crossword need not be moved off the screen.
V. The Spectrum BASIC Handbook (Vickers, 1982) gives a program to draw the
Union flag. Write programs or use the character generator and the diagram
routines (chapters 5 and 6) to produce other flags. You can draw company logos,
or even design new ones. Use the techniques in chapter 13 for accessing display-
236 Advanced Graphics with the SinclairZX Spectrum
me locations to add an extra option to the diagram routines. This option allows
you to copy a set of character blocks (already on the screen and specified by
'cursor') on to another set of blocks of the same size elsewhere on the screen
(also specified by 'cursor'). You.could even rotate or reflect them!
VI. The Spectrum BASICHandbook (Vickers, 1982) shows how to use BEEP
to create music(?). While BEEP is making the sounds, you can draw the musical
notation on the screen. Construct the staves and then use special characters to
place quavers, minims, etc. on the screen. The old music hall method of the
'bouncing ball' could be used to beat the time of the tune .
VII. Use the character block method to draw mazes. Naturally your program
must generate mazes with real solutions. Give yourself time limits for getting
through the maze. You can make the mazes dynamic, so that they change as the
game progresses. Add extra problems : man-eating monsters that roam the maze ;
holes that suddenly appear and can swallow you up: 'space warps' that can
transfer you anywhere in the maze if you do not move fast enough .
VIII. Extend the ideas of chapter 6 . Draw your own special histograms , pie-
charts and graphs. Make them dynamic (either by the 'movie' method of chapter
13, or the 'worm game' method of chapter 1, and its extended form in chapter
14). Generate whole sets of special characters. Create (apparent) three-dimen-
sional histograms by drawing every bar in three different-coloured sections. The
height of the front section of each bar must be a whole number of character
blocks and two character blocks wide. A side section is one block wide, the
same height as the first section, with a triangular hat filling the bottom-right half
of a block. The third section lies above the first section and is in the shape of a
rhombus that touches the first two sections. This ensures that there are never
more than two colours in any character block, and makes the bar look like an
orthographic view of a rectangular block .
IX. Create patterns. Use OVER with large numbers of random lines about the
screen. Or draw lines in a dense but regular way to get Moire patterns. For
example, draw lines joining the points (0 , I) to (255, 175 - I) for 0 ~ I ~ 175,
and points (1,0) to (255 - I, 175) for 0 ~ I ~ 255. Extend the ideas of the pat-
tern program of chapter 5 to produce complex symmetrical patterns - any
introductory book on crystallography (for example, see Phillips, 1956) will
give you lots of ideas.
X. The crystallography books (for example , Phillips, 1956) will give you many
ideas for three-dimensional objects. Extend into four dimensions - vertices are
simply a vector of four numbers and they require 5 X 5 matrices for transforma-
tions. For an orthographic projection of a four-dimensional point, we simply
ignore two of the coordinates (as opposed to one, z, in three dimensions). What
are translation, scale and rotation in four dimensions?
Projects 237
XI. We have already presented two board games, Chess and Master Mind. There
are many more possibilities : draughts (or checkers), Scrabble, Hangman, ludo.
You can create a compendium of games. The Spectrum can simply act as the
board, or it can also be a referee . If you feel really adventurous it can even act
as an opponent.
XII . Use special characters to construct a deck of playing cards. These can be
incorporated into a program to play blackjack (or pontoon) with the Spectrum
acting as the bank and opponent.
XIII. You can draw certain types of brain-teasers on your television. For ex-.
ample, suppose you have nine squares and each is divided into quarters down
the diagonals. Each quarter has a colour (blue "1", red "2", magenta "3" and
green "4") and a sign ("+" or "-"). We represent each square as a sequence of
four numbers that denote the areas taken clockwise around the centre . For
example , we could have (-1, -2, 1,4), (-1,3,4, -2), (1, -4, -2, 3), (1,2,
-- 3, -4), (1,3, -2 , -4), (1,4, -3, -2), (2, - 3, -4, 3) and two occurrences of
(-1, -4 ,3,2). The problem is to place the nine squares in a three-by-three
arrangement, so that if two quarters on neighbouring squares touch, then they
must be of the same colour but of opposite sign. You can use the Spectrum to
draw the squares initially on the left side of the screen and a three-by-three grid
of the same sizes on the right. Then you'take squares from the left and place
them in the grid, or replace them back to the left.
Write a program to find a solution of the above problem - it takes about 10
minutes to run, and finds two independent solutions .
XV. Write a PAC MAN type of video game. This involves drawing five moving
objects on the screen at a time. In order to make the game move faster, allow
only two of the ghosts to move with each move of PAC MAN. The ghosts should
find the shortest path towards the player when they are in hunting mode, and the
best escape route in running mode. Because of the complex layout of the screen,
you will have to compromise . Simply move towards (or away from) the player if
there is no wall in the way. Find a quick way of coding their movements as this
will be the most time -consuming part of the game. Speed is the essence of a
238 Advanced Graphics with the SinclairZX Spectrum
good video game. Perhaps a simple machine-code routine could be used to print
all five figures on the screen after altering their 'PRINT AT' positions.
XVI. Write a program that first (OVER)prints a graphics menu of special symbols
on the left-hand side of the screen; for example, the stylised components for
electronic circuits (resistors, capacitors, etc .). These symbols should consist of
groups of character blocks. Use a cursor to point at any menu-symbol and then
(using OVER) drag a copy ofit to a required position on the screen. Also add a
facility for drawing connecting lines and labelling with thin numeric and special
characters (for example, n for ohms). You should also allow deletion of symbols
inadvertently placed in the wrong position. Extra options could include saving
and loading, as well as deleting the menu from the final diagram.
XVII. In all our perspective diagrams it is assumed that the objects lie totally
in front of the eye. Change our programs so that they deal with the general
case where vertices may be behind the eye. See Newman and Sproull (1973)
concerning this three-dimensional clipping.
Appendix A Implementing Programs
on the 16K Spectrum
Over 7K of this machine is used for the display and attribute ftles and for
system variables. This automatically limits the size of programs and data to
about 8.8K. Many of the programs given in this book far exceed this value,
although most will run if the reader obeys the following proposals.
(1) Delete all REMarks from the tape listings and any unused routines from
library ftles (for example, 'plot' and possibly 'scale') before MERG(E)ing the
routines. Also follow the hints given in chapter 13 for optimising the program
code . For example, the program for drawing the jet (listings 'lib 1" 'lib3' and
'9.9') will just fit into the store if the REMs, 'scale' and 'plot' are deleted.
(2) It is possible that there will not be enough store for the four alternative
character sets (sets 2 to 5) and the User-Defined Graphics set (set 6). If there is
enough store the address table for the six sets (set I is the standard set) should
hold the values 15360,29271,30039,30807,31575 and 32080 respectively. It
is possible that the use of sets with the lower addresses could corrupt your pro-
gram and data, or even crash the computer. Use only the sets that do not
interfere with the store! The CLEAR statement found at the beginning of pro-
grams that use alternate characters must be changed from CLEAR 62294 to
CLEAR 255 + the address of the lowest set available (greater than 1). You will
fmd that the CHARACTER GENERATOR program does not have enough space
for set 2, and in this case we must CLEAR 255 + 30039. If you have a program
that requires set 2 then create these characters as set 5 (say), store it on tape, and
load it into your program, which must, of course , have space for set 2.
(3) You can break down programs into non-interdependent parts and store the
pictures and/or data produced by them on tape. These ftles may be reloaded in
conjunction with the other programs for further manipulation; for example, the
diagram constructions of chapter 6. You should LOAD 'Iibdiag' and MERGE one
of the histogram, pie-chart, graph or picture-editing programs. When you run the
program you must remember not to attempt to use routines that are not cur-
rently in memory. You can SAVE and LOAD the intermediate pictures using the
SCREEN$ option, and arrays using the DATA option. You can then LOAD the
picture-editing routines to finish off your diagrams.
(4) Unfortunately, some programs will not fit into 8.8K of memory whatever is
240 Advanced Graphics with the Sinclair ZX Spectrum
done; these are the general hidden line algorithm for non-trivial objects, the
movie program and the ISLAND DEFENCE game. If you are serious about
studying computer graphics on the Spectrum we would strongly advise you to
buy the 32K expansion for your machine .
Appendix B BASIC Program Listings
We now give a list of the BASIC program listings stored on the companion audio-
cassette tape . These routines are in the form necessary for running on the 48K
version ofthe Spectrum. Most of them need no changes in order to run on a
16K machine. However , if you have this type of machine you should check the
REMarks in listings given in the book for any changes, and read appendix A.
'movie', the five frames 'sphere 1'to 'sphereS' and ISLAND DEFENCE are the
only routines that are 48 K specific . You will find that with chapter 6 you have
to LOAD 'libdiag' and MERGE just one of '6.8 ', '6 .9', '6.10 &11' and '6.12' in
order for it to fit in the store. Should you need more than one type of data
graph on the screen at anyone time, you must intermediately SAVE picture on
tape and reLOAD the screen after a new program has been created.
SIDE 1
FileName Contents
directory 1
lib 1 Listings 2.1 ,2.2,2.3,2.4,2.8 and 3.3 ;
routines for mapping two-dimensional Euclidean space on to
graphics area.
2 .9 Listing 2.9;joining points of regular N-gon.
2.12 Example of envelope.
2.13 Spirograph.
3 .1 Square inside square inside square etc.
lib2 Listings 3.4, 4.1a, 4.2a, 4.3a, 4.4a, 4.5, 4 .6;
routines for matrix manipulation of two-dimensional space.
4.10 Construction of a general ellipse.
44.12
.11} Construction and view of four 'space ships'.
7.1 Point of intersection of line and plane in three-dimensional
space.
7.2 Point of intersection of two lines in three-dimensional space.
7.3&4 Example of dot and vector product.
7.5 Inverse of a 3 X 3 matrix.
7.6 Point of intersection of three planes in three-dimensional space.
7.7 Line of intersection of two planes in three-dimensional space.
lib3 Listings 3.4, 9.1,9 .2 and efficient rewrites of 8.1,8.2,8.3 and
8.4; routines for matrix manipulation of three-dimensionallspace.
242 Advanced Graphics with the ZX Spectrum
:~~:;:~}
sphere3 Five frames needed to make a movie of a rotating sphere.
sphere4
sphereS
SIDE 2
FileName Contents
directory 2
delete }
Utilities from listing 13.9.
renumber
5.5 The CHARACTER GENERA TOR.
5.4 Main program for simple tessellated patterns.
libdiag Listings 6.1,6.4,6 .6 ; support routines for diagram construc-
tion.
6.2 'paper' and 'ink'.
6.3 'point' and 'line'.
6.5&7 'label', 'thin' and 'create'
6.8 Histogram routine/type 1
6.9 Histogram routine/type 2
6.10&11 Pie-chart and hatching routines .
6.12 Scientific graph routine.
thin3 Characters stored in set 3 and used to make thin characters.
thin4 Characters stored in set 4 and used to make thin characters.
1.16 The WORM GAME.
5.6 MASTER MIND program.
masterset Characters needed by MASTER MIND program.
5.7 The Chess board program .
Appendix B BASIC Program Listings 243
References
Ahl, D. H. (Ed.) (1980). Basic Computer Games. Workman Publishing Co., New
York
Bain, G. (1972). Celtic Art: The Methods of Construction, 2nd edition.
Maclellan, Glasgow
Cohn, P. M. (1961). Solid Geometry . Routledge and Kegan Paul, London
Coxeter, H. S. M. (1974). Regular Polytopes . Dover Publications, New York
Davenport, H. (1952). The Higher Arithmetic . Hutchinson, London
Finkbeiner, D. T. (1978). Introduction to Matrices and Linear Transformations,
3rd edition. W. H. Freeman, San Francisco
Horowitz, E. and Sahni, S. (1976). Fundamentals ofData Structures. Pitman,
London
Hurley , R. (1982). More Real Applications for the ZX81 and the ZX Spectrum.
Macmillan, London
Hutty, R . (1981). Z80 Assembly Language Programming for Students, 2nd
edition. Macmillan, London
Knuth, D. (1972,1973,1981). The Art of Computer Programming. Volume 1:
Fundamental Algorithms, 2nd edition, 1973. Volume 2: Semi-numerical
Algorithms, 2nd edition, 1981. Volume 3: Sorting and Searching , 1972.
Addison-Wesley, London
Liffick , B. W. (1979). The BYTE Book ofPascal. Byte Publications, New
Hampshire
Mandelbrot, B. B. (1977). Fractals. W. H. Freeman, San Francisco
McCrae, W. H. (1953). Analytical Geometry of Three Dimensions. Oliver and
Boyd,London
Newman, W. M. and Sproull, R. F. (1973). Principles ofInteractive Computer
Graphics. McGraw-Hill, London
Phillips, F. C. (1956). An Introduction to Crystallography, 2nd edition.
Longmans, London
Stroud, K. A. (1982). Engineering Mathematics , 2nd edition. Macmillan , London
Tolansky, S. (1964). Optical Illusions, Pergamon, New York
Vickers, S. (1982). Sinclair ZX Spectrum: Basic Programming. Sinclair Research,
Cambridge
References and Further Reading 245
Further Reading