Graphics Series

We treat user defined characters in a colourful and seasonal way in the latest of the series.

Volume 1

Number 10

December 1983

Meet the pixels on your Christmas tree

By MICHAEL NOELS

THIS MONTH we are going to use the graphic methods we've already discussed, plus some new ones, to produce a seasonal offering — a Christmas tree with an appropriate greeting.

The finished product is Program VII. Before that, though, we'd better look at a few of the techniques it uses, the most important of which concern user defined characters.

These characters, as their name implies, allow you to use the PRINT statement (or VDU) to print out your own special characters on the screen.

In the BBC Micro's graphics modes, all the characters are drawn on an 8 x 8 grid, each cell of the grid being known as a pixel.

Figure I. Pixel pattern for the letter A

Figure I illustrates how the grid for the letter A is filled out. To print this letter by using its Ascii code simply enter:

PRINT CHR$(65)

Now the BBC Micro has several blank grids ready for you to fill. These grids have the Ascii codes 224 to 255.

Once you've filled, say, 224 with the required pattern of cells, to print out this user defined character, you type:

PRINT CHR$(224)

and it appears where the cursor is, with the appropriate foreground and background colours.

Figure I illustrates how you can define your own character. You simply fill in the cells of the 8 x 8 grid until you get the pattern required. Then all that's needed is a few simple sums.

Each column of the grid has a value. The value of the rightmost column is 1, the one to its left 2, the one to the left of that 4, and so on, doubling each time until the leftmost column has a value of 128.

You may have noticed that these are the column values of a binary number.

Each row of the grid has a number calculated for it and you add together the column values of each cell shaded in that row.

Figure II. User defined characters

Figure II shows the idea. Each row has its number. To teach the computer this pattern we use:

VDU 23

followed by the Ascii code we're going to use for it followed by the eight numbers we've calculated for the rows, starting with the top and working down. All these are separated by commas.

So, to define character 224 to be the pattern shown in Figure II, we use:

VDU 23,224,153,66,36,153,153,36,66,153

Program I makes use of this character.

10 REM *** PROGRAM I ***
20 MODE 4
30 VDU 23,224,153,66,36,153,153,36, 66,153
40 FOR I%=0 TO 19
50 PRINT TAB(I%,I%)CHR$(224)
60 NEXT I%

Try altering line 20 to:

20 MODE 5

and notice the difference — the character is spread sideways. This is because the shape of the pixels in Mode 5 differs from that in Mode 4.

A pixel is the smallest point you can plot on a screen. Its size limits the resolution, or fineness, available.

Figure III. Pixel sizes in each graphic mode

As Figure III shows, in Modes 1 and 4, the pixel is four graphic units (gu) square, whereas in Modes 2,5 and 0 it is variously oblong.

So when you print out an 8 x 8 pixel grid in Mode 4 it comes out square, whereas in Mode 5 the wider pixels stretch the figure.

Figure IV. Character sizes in each graphic mode

Figure IV shows the size of a character in each graphic mode in terms of graphical units.

You have to allow for this alteration in width when you are visualising your user defined character. I find it easier to see the character I want in terms of square pixels, so I've stuck to Mode 1 in Program VII even though it limits the range of available colours.

Let's try to print a pattern with our user defined character. We'll simply print the character three times, in a vertical line.

We can use the cursor control codes to ensure that the characters actually end up vertically aligned.

VDU 8 moves the cursor left
VDU 9 moves the cursor right
VDU 10 moves the cursor down
VDU 11 moves the cursor up

Program II illustrates the technique.

10 REM *** PROGRAM II ***
20 MODE 4
30 VDU 23,224,153,66,36,153,153,36, 66,153
40 VDU 224,8,10,224,8,10,224

Remember that we can string VDU statements together. Line 40 works like this: 224 prints the character, leaving the cursor directly to its right. 8 moves the cursor back left and 10 moves the cursor vertically down, leaving it directly under the first character. 224 prints the character again. 8 moves the cursor left and 10 moves the cursor down so that 224 prints the character vertically under the other two.

We can also use:

PRINT CHR$(8)

to move the cursor left, and so on for the other cursor movements. Although slightly longer, this has the advantage that we can add all the characters, user defined and cursor movement, into one string and then simply print out that string.

This results in the combined characters appearing far more quickly than with the VDU method. (See Program III.)

10 REM *** PROGRAM III ***
20 MODE 4
30 VDU 23,224,153,66,36,153,153,36, 66,153
40 character$=CHR$(224)+CHR$(8)+CHR $(10)+CHR$(224)+CHR$(8)+CHR$(10)+CHR$( 224)
50 PRINT character$

We use this technique in Program VII. Lines 210 to 290 each add together the user defined characters required to make the various shapes. Line 210, for example, defines a candle.

Notice how I use the name candle$ for the combined character string - it's far more meaningful than, say, shapel$.

In Program VII, the actual user defined characters are defined in lines 170 to 210, which read in the values for each VDU 23 from the DATA statements in lines 930 to 1060, 'VDUing' each parameter at a time (line 190).

Notice how -1 is used in line 1060's DATA statement to mark the end of the parameters.

When we print a user defined character, it prints, as we have seen, in the currently defined foreground and background colour.

Suppose, however, that we want a two-colour foreground, such that the centre pixels of our previous character and those in the middle of the outer edges are yellow, while the other pixels are red. (We'll leave the background black.)

You might think that we could do this by defining our character (224) as before and printing it in red, then overprinting it in yellow with another user defined character (225) containing only the pixels we want to be yellow. The red pixels should then show through.

Figure V. Overprinting user defined characters

Figure V illustrates the idea. However, it isn't as straightforward as this, as Program IV shows.

10 REM *** PROGRAM IV ***
20 MODE 1
30 VDU 23,224,153,66,36,153,153,36, 66,153
40 VDU 23,225,24,0,0,153,153,0,0,24
50 COLOUR 1
60 PRINT TAB(19,15)CHR$(224)
70 COLOUR 2
80 PRINT TAB(19,15)CHRI(225)

What happens is that the second character's background is printed, as well as its foreground, completely overwriting the first character!

We need some way of writing just the foreground of the characters - making the background transparent.

We can do this by using the command VDU 5. After such a command, the micro only prints the foreground of characters. This works not only for user defined characters, but for the whole character set as well.

There is one snag, though. VDU 5 also causes the characters to be printed not at the text cursor, but where the graphics cursor is.

Technically, we say that VDU 5 joins the text and graphics cursors. However, it's the graphics commands that are used to position the joined cursors - and to set colours - so I consider that VDU 5 causes characters to be printed at the graphics cursor.

This means that, after a VDU 5, to print a character at a certain location on the screen you have to place the graphics cursor there with the MOVE statement before PRINTing.

The numbers involved might take a bit more thought, but you gain two advantages. Firstly, you can position your character to the resolution of the screen, that is, within one pixel. With the TAB statement, you are limited to the eight pixel resolution of the character grid.

Secondly, of course, you only print the foreground of the character-there's no background to overwrite things.

Another useful aspect of operations after VDU 5 is that the cursor movement characters still work. For example, CHRS(8) still moves the graphics/text cursor one whole character (eight pixels) left from its previous position. Try Program V.

10 REM *** PROGRAM V ***
20 MODE 1
30 VDU 5
40 VDU 23,224,153,66,36,153,153,36, 66,153
50 VDU 23,225,24,0,0,153,153,0,0,24
60 GCOL0,1
70 MOVE 640,512:PRINT CHR$(224)
80 GCOL0,2
90 MOVE 640,512:PRINT CHR$(225)
100 VDU 4

Notice the use of VDU 4 at the end -this turns off the VDU 5. You should always tidy up this way - leaving VDU 5 in operation can cause problems. Try listing a long program with VDU 5 on to see what I mean.

Also, the relevant colours are the graphics colours, not the text colours, so we use GCOLO instead of COLOUR.

A word of warning. Don't use TAB with VDU 5 on. Although you can get away with it in OS 0.1, your program won't work when you upgrade to later systems.

Now run Program VI.

10 REM *** PROGRAM VI ***
20 MODE 1
30 VDU 5
40 VDU 23,224,153,66,36,153,153,36, 66,153
50 VDU 23,225,24,0,0,153,153,0,0,24
60 character$=CHR$(18)+CHR$(0)+CHR$ (1)+CHR$(224)+CHR$(18)+CHR$(0)+CHR$(2) +CHR$(8)+CHR$(225)
70 MOVE 640,512
80 PRINT character$
90 VDU 4
100 VDU 4

This has the same effect as Program V. In this case we define a string to print out our multi coloured character (line 50). The latter might need some explanation.

Instead of using GCOL0,1 we can:

PRINT CHR$(l8) + CHR$(0) + CHR$(1) where CHR$(18) is in place of GCOL

CHR$(0) is in place of 0 and CHR$(1) is in place of 1 So, the first four CHR$ statements of line 60 give the foreground colour 1 (red), followed by the user defined character (224). The next three CHR$ statements change the graphics colour to logical colour 2 (yellow), then CHR$(8) performs a backspace, ensuring that CHR$(225) overprints the first character.

We use this technique in Program VII to obtain our fairy. Line 270 adds together the string fairy$, consisting of its body. This is printed over wfairy$ which is the fairy's body plus wings (PROCfairy, 710-780).

fairy$ and wfairy$ each consist of nine user defined characters, in a 3 x 3 grid. Actually, two of the characters of fairy$ (235 and 237) are completely blank. Since one variable overprints the other, I decided to keep the grid standard for easier handling.

Similarly, we print a filled-in ball, fball$, followed by its outer rim, uball$, on top of it (PROCballs, 590-690).

The candles are printed out as a combined string (candle$) in PROC-candles (490-570).

The actual tree shape is drawn by printing three green triangles, the apexes of which overlap (PROCtriangles, 320-390). The base of the tree is drawn in PROCbase (410-470).

PROCmessage simply prints out a seasonal greeting, using VDU 5 to give a sort of 3D effect. Program VIII illustrates the technique.

What it does is to print out 'A WORD' in yellow several times, each time moving it down four graphic units to the left and down (line 60). This is because, each time through the loop, the number we reduce the X and Y coordinates by, 1%, is increased by 4.

The reason we move four graphic units at a time is because this is the minimum resolution in Mode 1.

When you exit a loop, the loop variable is still incremented before the check to see if the loop should be terminated is made. So, when line 90 is reached, 1% is four greater than when the loop last printed 'A WORD'. Also the colour has changed to red (line 100) so line 110 prints out 'A WORD' in red, one pixel down and to the left of the last 'A WORD'.

Try to decipher Program VII -working through other people's programs is one of the best ways to improve your own programming.

Next month, after this seasonal diversion, we'll return to the graphics straight and narrow with the GCOL statements I promised!