Task 8  Task 8: BASIC Graphics  Task 8


BASIC originally came with a very primative set of graphics commands at the programmer's disposal. There are commands to create dots, lines, boxes, circles and painting areas of the screen. Even though the commands are very simple a creative programmer can do some very powerful and interesting things with them. In this task we'll cover these primative commands and in a later task step it up with some really fancy graphics that QB64 has introduced to the BASIC language.

-- The Graphics Screen --

Up to this point we have been doing everything in a text only screen where graphics are not permitted, but the point of this course is to get you into game programming. While the earliest BASIC games were text adventures I'm guessing games like that just won't cut it in today's graphics dominated gaming world. The first thing you'll need to learn how to do is put QB64 into a graphics screen mode. The SCREEN statement is used to do this. SCREEN 0 is the text mode screen we have been working in up to this point and is considered the default screen. Below is a listing of the original legacy graphic screens available in QuickBASIC and their characteristics.

Screen
Mode
TextGraphics (pixels)ColorsVideo
Pages
Text
Block
Default
QB64 Font
RowsColumnsWidthHeightAttributesBPP
025/43/5080/40No graphics, text only16/1640 - 7n/a_FONT 16
125403202004/164none8 X 8_FONT 8
225806402002/Monochrome1none8 X 8_FONT 8
7254032020016/1640 - 78 X 8_FONT 8
8258064020016/1640 - 38 X 8_FONT 8
9258064035016/6440 - 18 X 14_FONT 14
1025806403504/2 Grayscale2none8 X 14_FONT 14
1130/60806404802/Monochrome1none8 X 16_FONT 16
1230/608064048016/65K4none8 X 16_FONT 16
132540320200256/262K8none8 X 8_FONT8
Chart 1 - QuickBASIC legacy graphic screens (before the introduction of QB64)

Let's cover each column's meaning:

Screen Mode - Issueing the SCREEN statement with the mode number after activates this graphics screen.

Text (Rows/Columns) - The number of text rows and columns available to the corresponding screen mode.

Graphics (Width/Height) - The number of pixels, or dots, available on the screen. For instance, a graphics screen of 320 pixels wide by 200 pixels high gives the programmer 64,000 pixels to work with (320 times 200 equals 64,000).

Colors (Attributes/BPP) - The number of background/foreground colors allowed to be shown on the screen at one time. BPP stands for bits per pixel and refers to the bit depth of each color. Four bits, 1111, can represent 16 different combinations while eight bits, 11111111, can represent up to 256 different combinations. So a screen of 320 pixels wide by 200 pixels high with a bit depth of 8 is 500KB in size (320 times 200 times 8 equals 512,000).

Video Pages - The number of individual pages available to each screen. Think of it like this, a screen with 8 video pages (0 - 7) is like having 8 sheets of paper on top of each other. You can draw something on all 8 of them, but only one of the pages can be on top of the stack at any given time. That page on top of the stack is the one that is viewed but any of the other 7 can be pulled from the stack and placed on top at will. Most games achieve animation using such a technique; while one page, or frame, is being viewed, the next page, or frame, is being drawn and then brought forward to be viewed when finished, and so on.

Text Block - The size, in pixels, that a text character occupies on the screen. For instance, a screen 320 pixels wide by 200 pixels high can have 25 rows by 40 columns of text because the text block is 8 X 8 (320 divided by 8 equals 40 columns, 200 divided by 8 equals 25 rows).

Default QB64 Font - We'll talk about this in a later task.


-- Points of Light --

The largest graphics screen available to the QuickBASIC coder was screen 12 with dimensions of 640 by 480 pixels allowing up to 16 colors at a time on the screen. Think of a graphics screen as a sheet of graph paper with each individual square on the paper being a pixel. A pixel is the smallest point that can be referenced on a graphics screen and is literally a single point of colored light you can control. A location on a graphics screen is referenced as an X,Y coordinate pair with each location containing a single pixel. The upper left hand corner of a graphics screen is coordinate 0,0. Go ahead and take a few minutes to type the following program in and then execute it to show this.

_TITLE "Screen 12 Properties" ' give the window a title

SCREEN 12 '                     switch into a graphics screen

PSET (0, 0), 15 '               turn on white pixels
PSET (319, 239), 15
PSET (639, 479), 15
LINE (5, 5)-(50, 50), 14 '      draw yellow lines
LINE (5, 5)-(5, 15), 14
LINE (5, 5)-(15, 5), 14
LINE (319, 234)-(319, 189), 14
LINE (319, 234)-(309, 224), 14
LINE (319, 234)-(329, 224), 14
LINE (634, 474)-(584, 424), 14
LINE (634, 474)-(624, 474), 14
LINE (634, 474)-(634, 464), 14
LINE (0, 279)-(639, 279), 14
LINE (0, 279)-(10, 269), 14
LINE (0, 279)-(10, 289), 14
LINE (639, 279)-(629, 269), 14
LINE (639, 279)-(629, 289), 14
LINE (150, 0)-(150, 479), 14
LINE (150, 0)-(160, 10), 14
LINE (150, 0)-(140, 10), 14
LINE (150, 479)-(160, 469), 14
LINE (150, 479)-(140, 469), 14
LOCATE 4, 2 '                   place text on screen
PRINT "Coordinate 0,0"
LOCATE 5, 3
PRINT "(White dot)"
LOCATE 11, 34
PRINT "Center 319,239"
LOCATE 12, 35
PRINT "(White dot)"
LOCATE 26, 62
PRINT "Coordinate 639,479"
LOCATE 27, 66
PRINT "(White dot)"
LOCATE 18, 23
PRINT "Width of screen 640 pixels (0-639)"
LOCATE 25, 3
PRINT "Height of screen 480 pixels (0-479)"
SLEEP '                         wait for user to press a key
SYSTEM '                        return to Windows

Screen 12
Figure 1 - Screen 12 characteristics

Programming languages almost always treat zero as the starting point to any counting or coordinate system and this true with screen coordinates. Figure 1 clearly shows that screen 12 is 640 pixels wide by 480 pixels high, however the lower right coordinate is not referenced as 640,480 but instead as 639,479 because of the coordinate system starting at zero for both x and y.

The PSET command, which stands for point set, is used to control an individual coordinate position, or pixel. When a graphics screen is initiated all pixels are turned off (black) by default. In the example program above we use three PSET statements to change the color of three pixels to white, effectively making them appear to be turned on.


PSET (0, 0), 15 '               turn on white pixels
PSET (319, 239), 15
PSET (639, 479), 15


The first PSET statement turns on the upper left pixel located at coordinate (0, 0). The coordinates need to be surrounded by parenthesis. Following the coordinates comes the color you wish to make the pixel. In our case a value of 15 is used which indicates white as shown in Chart 2 below. The second PSET statement turns on the screen's center pixel and finally the third PSET statement turns on the lower right screen pixel. QuickBASIC had 16 standard colors to choose from:

0 - BLACK1 - BLUE2 - GREEN3 - CYAN (BLUE + GREEN)
4 - RED5 - MAGENTA (BLUE + RED)6 - DARK YELLOW (BROWN)7 - GRAY (DARK WHITE)
8 - DARK GRAY (BRIGHT BLACK)9 - BRIGHT BLUE10 - BRIGHT GREEN11 - BRIGHT CYAN
12 - BRIGHT RED13 - BRIGHT MAGENTA (PINK)14 - YELLOW (GREEN + RED)15 - WHITE (BLUE + GREEN + RED)
Chart 2 - QuickBASIC legacy color values

If no color attribute is given to a graphics command the default color of white (meaning all BPP color depth turned on) will be used.


-- Line Up Everyone --

The LINE command is used to, you guessed it, draw lines on a graphics screen. A line has a starting and ending point so you'll need to give the LINE statement a minimum of two coordinates to work with. The yellow arrow that is pointing toward coordinate 0,0 in the example program was drawn using these three LINE statements:

LINE (5, 5)-(50, 50), 14 '      draw yellow lines
LINE (5, 5)-(5, 15), 14
LINE (5, 5)-(15, 5), 14


The first LINE statement begins the line at coordinate (5, 5) and finishes the line at coordinate (50, 50) effectively making a line at a 45 degree angle. The two coordinates need to be separated by a dash, - , which is prounounced as "to" when reading the command. Finally, we end the LINE command with the color we wish to draw the line in. Here 14 was chosen which is the color yellow according to our color chart above. The second and third LINE commands start at the same coordinate (5, 5) as the first LINE command and draws a small vertical line , then a small horizontal line, effectively creating an arrow. The remaining LINE commands in the example are drawing their arrows in the same manner at different coordinates on the screen. Save this program as SCREEN12.bas now and then start a new program in the IDE.

The LINE command can also be used to draw rectangles and optionally fill them in with a solid color. The following program illustrates this:

_TITLE "The LINE Command"

SCREEN 12

LOCATE 2, 25
PRINT "LINE (49, 49)-(589, 429), 12"
LINE (49, 49)-(589, 429), 12
LOCATE 29, 34
PRINT "PRESS A KEY";
SLEEP
CLS
LOCATE 2, 24
PRINT "LINE (49, 49)-(589, 429), 12, B"
LINE (49, 49)-(589, 429), 12, B
LOCATE 29, 34
PRINT "PRESS A KEY";
SLEEP
CLS
LOCATE 2, 24
PRINT "LINE (49, 49)-(589, 429), 12, BF"
LINE (49, 49)-(589, 429), 12, BF
LOCATE 29, 34
PRINT "PRESS A KEY";
SLEEP

The second LINE statement:

LINE (49, 49)-(589, 429), 12, B

creates a box from the two coordinates given because of the optional ", B" parameter added to the end of the command. The third LINE statement:

LINE (49, 49)-(589, 429), 12, BF

creates a box filled from the two coordinates given because of the optional ", BF" parameter added to the end of the command. The fill color of the box will be the color number assigned to the line being drawn.

QB64 will also remember the last coordinate used by any of the graphics commands that use coordinates to place graphics on the screen. This remembered coordinate can then be used by the next graphics command. Save the previous example as LINEBF.bas before typing in the next example.

'--------------------------------
'- Variable Declaration Section -
'--------------------------------

DIM x1%, y1% '              upper left coordinates
DIM x2%, y2% '              lower right coordinates
DIM Count% '                FOR...NEXT loop counter

'----------------------------
'- Main Program Begins Here -
'----------------------------

_TITLE "Optical Illusion" ' give window a title
SCREEN 12 '                 initiate a graphics screen
x1% = 0 '                   set starting coordinates
y1% = 0 '                     to upper left and
x2% = 639 '                   lower right coordinates of
y2% = 479 '                   screen 12
PSET (x1%, y1%), 11 '       set first coordinate for QB64 to remember
FOR Count% = 1 TO 60 '      loop 60 times
    LINE -(x2%, y1%), 11 '  draw line from previous coordinate
    LINE -(x2%, y2%), 11 '  draw another line from previous coordinate
    LINE -(x1%, y2%), 11 '  draw another line from previous coordinate
    y1% = y1% + 4 '         increase first Y coordinate by 4
    x2% = x2% - 4 '         decrease second X coordinate by 4
    y2% = y2% - 4 '         decrease second Y coordinate by 4
    LINE -(x1%, y1%), 11 '  draw another line from previous coordinate
    x1% = x1% + 4 '         increase first X coordinate by 4
NEXT Count% '               draw again until loop count reaches 60
SLEEP

Doink!
Figure 2 - Making your eyes go doink!

In the previous example a PSET statement is used to set the first coordinate that QB64 will remember. The first LINE statement can use this remembered coordinate as its first coordinate through this modified use of the LINE command:

LINE -(x2%, y1%), 11

When the LINE statement sees that there is no starting coordinate supplied it simply uses any previous coordinate that QB64 is remembering. In this case the PSET statement provided the coordinate for QB64 to remember. Inside the loop each LINE statement is providing the next starting coordinate for any following LINE statement to use. By tweaking the starting and ending coordinate values inside the loop, x1%, y1%, x2% and y2%, we can create the square looping structure seen on the screen. Save this example program as ILLUSION.bas before continuing on.

Note: If a previous graphics command did not supply QB64 with coordinates to remember, QB64 will simply return 0, 0 (the upper left of screen) as the coordinates it is remembering by default.

-- Going In Circles --

The CIRCLE statement is used to draw circles onto a graphics screen (don't you just love BASIC's simplicity). The CIRCLE statement can draw full or partial circles and elongated ellipses as well. Let's take a look at the CIRCLE statement's structure as it can get a bit confusing:

CIRCLE [STEP](Column, Row), radius%[, colour%][, startRadian!, stopRadian!] [, aspect!]

Note: The use of STEP, available in most graphics commands, adds the values of Column and Row to the current coordinate x and y locations to achieve a new  x and y coordinate position. Also, the use of brackets [] around a statement's parameter, such as [, colour%], means that the parameter is optional or not mandatory for the statement to function.

At a minimum the CIRCLE statement requires a coordinate pair and a radius value to display a circle on the screen. More often than not you are also going to supply a color value as well because white circles are rather boring. Let's start by drawing a simple circle to a graphics screen:

SCREEN 12

CIRCLE (319, 239), 100, 14 '  draw a yellow circle with radius of 100 pixels

Circle
Figure 3 - A yellow circle

By default the CIRCLE statement draws a full 360 degree arc, otherwise known as a circle, with an aspect ratio of 1 (more on aspect ratios in a bit). However, you can over ride the default behavior of the CIRCLE statement to draw only part of the circle, known as an arc, by specifying the start and stop radian values of the arc. To better illustrate this type in the following program and execute it.

'--------------------------------
'- Variable Declaration Section -
'--------------------------------

DIM StartRadian! ' starting point of circle arc
DIM StopRadian! '  ending point of circle arc
DIM Aspect! '      aspect ratio of circle (ellipses)
DIM Pi! '          holds the value of PI
DIM KeyPress$ '    holds the value of user key presses
DIM Sign% '        holds the sign (-1 or +1) of arc radians

'----------------------------
'- Main Program Begins Here -
'----------------------------

_TITLE "Circle fun!" '                           give the window a title
SCREEN 12 '                                      initiate a graphics screen
Pi! = 3.1415926 '                                give variable the value of PI
StartRadian! = 2 * Pi! '                         set initial arc starting point
StopRadian! = 0 '                                set initial arc ending point
Aspect! = 1 '                                    set initial aspect ratio of circle
Sign% = 1 '                                      set radian sign

DO '                                             main loop begins here
    CLS '                                        clear the screen
    StopRadian! = StopRadian! + 2 * Pi! / 360 '  increment stop radian in 360ths
    IF StopRadian! > 2 * Pi! THEN '              has arc done a full sweep?
        StopRadian! = 0 '                        yes, reset the arc end point
    END IF
    LINE (319, 119)-(319, 359), 12 '             draw vertical red line
    LINE (199, 239)-(439, 239), 12 '             draw horizontal red line
    '
    ' draw the current arc on the screen
    '
    CIRCLE (319, 239), 100, 14, Sign% * StartRadian!, Sign% * StopRadian!, Aspect!
    '
    ' display various text on screen to user
    '
    LOCATE 2, 18
    PRINT "CIRCLE radian demonstration for creating arcs"
    LOCATE 4, 30
    IF Sign% = 1 THEN
        PRINT "Stop radian ="; StopRadian!
    ELSE
        PRINT "Stop radian ="; -StopRadian!
    END IF
    LOCATE 5, 29
    PRINT "Aspect ratio ="; Aspect!
    LOCATE 7, 32
    PRINT "Half PI 1.5707963"
    LOCATE 24, 32
    PRINT "1.5x PI 4.7123889"
    LOCATE 15, 57
    PRINT "0 or 2x PI 6.2831852"
    LOCATE 15, 13
    PRINT "PI 3.1415926"
    LOCATE 27, 3
    PRINT "Press SPACEBAR to change arc type, hold UP/DOWN arrow keys to change ellipse"
    LOCATE 29, 26
    PRINT "Press ESC key to exit program";
    KeyPress$ = INKEY$ '                         grab key the user may have pressed
    IF KeyPress$ = " " THEN '                    did user press space bar?
        Sign% = -Sign% '                         yes, change the sign
    END IF
    IF _KEYDOWN(18432) THEN '                    is user holding UP arrow?
        Aspect! = Aspect! + .005 '               yes, increment aspect ratio
        IF Aspect! > 3 THEN '                    is aspect ratio greater than 3?
            Aspect! = 3 '                        yes, limit its value to 3
        END IF
    END IF
    IF _KEYDOWN(20480) THEN '                    is user holding DOWN arrow?
        Aspect! = Aspect! - .005 '               yes, decrement aspect ratio
        IF Aspect! < .25 THEN '                  is aspect ratio less than .25?
            Aspect! = .25 '                      yes, limit its value to .25
        END IF
    END IF
    _LIMIT 60 '                                  limit loop to 60 FPS
    _DISPLAY '                                   update screen with changes
LOOP UNTIL KeyPress$ = CHR$(27) '                leave loop when ESC key pressed
SYSTEM '                                         return to Windows

Circle Demo
Figure 4 - The CIRCLE demonstration program

As the previous example shows radians range from 0 to 6.2831852 which is 2*pi. When the CIRCLE statement is supplied with positive radian values only the arc is drawn, but when the radians supplied are negative a line from the start radian and end radian is drawn back to the center of the circle, creating what looks like a piece of pie. The start and end radian values can be located anywhere on the circle's arc, but remember that the arc is always drawn in a counter-clockwise fashion. For instance, if you were to designate pi as the start value and half pi as the end value the end result would not be an arc contained in the upper left quadrant of the circle. In fact, the arc would include the entire circle except for this quadrant, because of the arc being drawn counter-clockwise.

A radian is a point location on a circle's circumference and always falls between the values of 0 to 2*pi. The circumference of any circle is always 2*pi*radius and because of this relationship between a circle's radius and circumference a point on the circumference can be computed by supplying a radian value. The CIRCLE statement does this math for you and displays the result as an arc. This Wikipedia entry on radians does a very good job of explaining this relationship if you have not yet encountered it in your math classes.

The example program also shows you how you can affect the CIRCLE statement's aspect ratio of a circle or arc. The default value of 1 for the aspect ratio results in a perfectly round circle displayed on the screen. Smaller values display squashed ellipses while larger values display taller ellipses. It may appear that you must supply a start and end radian to the CIRCLE statement in order to supply an aspect value, but you can simply skip over these two entries like so:

CIRCLE (319, 239), 100, 14, , , 2 ' create a tall yellow ellipse

By supplying nothing for start and end radian the CIRCLE statement simply uses default values that create a complete circle. Save this example program as RADIANS.bas before continuing.


Here is an example program that shows the power of the CIRCLE statement and how creative programming can use statements in ways that seem to defy their purpose. This example uses CIRCLE to draw straight lines creating the hands of a clock and the tick marks going around the clock face.

'-------------------------------------------------------
'Description: Draws a functioning clock using circles
'Author     : Terry Ritchie
'Date       : 11/12/13
'Version    : 2.0
'Rights     : Open source, free to modify and distribute
'Terms      : Original author's name must remain intact
'-------------------------------------------------------

'--------------------------------
'- Variable Declaration Section -
'--------------------------------

DIM Clock!(60) ' 60 radian points around circle
DIM Tick% '      counter to keep track of tick marks
DIM Tc% '        tick mark color
DIM Radian! '    FOR...NEXT radian counter
DIM h% '         value of hour extracted from TIME$
DIM m% '         value of minute extracted from TIME$
DIM s% '         value of second extracted from TIME$
DIM Tm$ '        current TIME$ value

'----------------------------
'- Main Program Begins Here -
'----------------------------

SCREEN 12 '                                                          initiate graphics screen
Tick% = 15 '                                                         first position is 15 seconds
FOR Radian! = 6.2831852 TO 0 STEP -.10471975 '                       clockwise from 2*Pi in steps of -60ths
    Clock!(Tick%) = Radian! '                                        save circle radian seconds position
    Tick% = Tick% + 1 '                                              move to next seconds position
    IF Tick% = 60 THEN Tick% = 0 '                                   reset to 0 seconds position if needed
NEXT Radian!
DO '                                                                 begin main loop
    CLS '                                                            clear the screen
    _LIMIT 5 '                                                       5 updates per second
    CIRCLE (319, 239), 120, 7 '                                      draw outer circle of clock
    PAINT (319, 239), 8, 7 '                                         paint circle dark gray
    CIRCLE (319, 239), 110, 0 '                                      draw inner circle of clock
    PAINT (319, 239), 15, 0 '                                        paint face bright white
    FOR Tick% = 0 TO 59 '                                            cycle through radian seconds positions
        IF Tick% MOD 5 = 0 THEN Tc% = 0 ELSE Tc% = 7 '               if a 5 second position color is black
        CIRCLE (319, 239), 109, Tc%, -Clock!(Tick%), Clock!(Tick%) ' draw radian line from center of circle
    NEXT Tick%
    CIRCLE (319, 239), 102, 8 '                                      draw circle to cut off radian lines
    PAINT (319, 239), 15, 8 '                                        paint face again to remove radian lines
    CIRCLE (319, 239), 102, 15 '                                     fill in cut off circle
    CIRCLE (319, 239), 4, 0 '                                        draw small black circle in center
    PAINT (319, 239), 0, 0 '                                         paint small black circle
    Tm$ = TIME$ '                                                    get current time from TIME$
    s% = VAL(RIGHT$(Tm$, 2)) '                                       get numeric value of seconds
    m% = VAL(MID$(Tm$, 4, 2)) '                                      get numeric value of minutes
    h% = VAL(LEFT$(Tm$, 2)) '                                        get numeric value of hours
    IF h% >= 12 THEN h% = h% - 12 '                                  convert from military time
    COLOR 0, 15 '                                                    black text on bright white background
    LOCATE 19, 37 '                                                  position cursor on screen
    PRINT RIGHT$("0" + LTRIM$(STR$(h%)), 2) + RIGHT$(Tm$, 6) '       print current time in face of clock
    COLOR 7, 0 '                                                     white text on black background
    Tick% = (h% * 5) + (m% \ 12) '                                   calculate which hour hand radian to use
    CIRCLE (319, 239), 80, 0, -Clock(Tick%), Clock(Tick%) '          display hour hand
    h% = h% + 6 '                                                    move to opposite hour on clock face
    IF h% >= 12 THEN h% = h% - 12 '                                  adjust hour if necessary
    Tick% = (h% * 5) + (m% \ 12) '                                   calculate which hour hand radian to use
    CIRCLE (319, 239), 15, 0, -Clock(Tick%), Clock(Tick%) '          display small opposite tail of hour hand
    CIRCLE (319, 239), 95, 0, -Clock(m%), Clock(m%) '                display minute hand
    m% = m% + 30 '                                                   move to opposite minute on clock face
    IF m% > 59 THEN m% = m% - 60 '                                   adjust minute if necessary
    CIRCLE (319, 239), 15, 0, -Clock(m%), Clock(m%) '                display small opposite tail of min hand
    CIRCLE (319, 239), 2, 4 '                                        draw small red circle in center
    PAINT (319, 239), 4, 4 '                                         paint small red circle
    CIRCLE (319, 239), 100, 4, -Clock(s%), Clock(s%) '               draw second hand
    s% = s% + 30 '                                                   move to opposite second on clock face
    IF s% > 59 THEN s% = s% - 60 '                                   adjust second if necessary
    CIRCLE (319, 239), 25, 4, -Clock(s%), Clock(s%) '                display small opposite tail of sec hand
    CIRCLE (319, 239), 1, 14 '                                       draw small yellow circle in center
    PSET (319, 239), 14 '                                            fill in small yellow circle
    _DISPLAY '                                                       update screen with loop's changes
LOOP UNTIL INKEY$ <> "" '                                            end program when key pressed
SYSTEM '                                                             return to Windows

CircleClock
A working clock drawn entirely with the CIRCLE command!

-- Paint By Numbers --

The LINE statement offered a way to create filled boxes with a solid color however the CIRCLE statement does not. Also, how would a programmer go about filling in an irregular shaped object created by multiple LINE statements? Well, BASIC has an answer for this problem with the PAINT statement. Type in the following program to see the PAINT statement work its magic.

SCREEN 12

LINE (50, 50)-(300, 300), 12, BF
CIRCLE (319, 239), 100, 2
PAINT (319, 239), 10, 2

Pretty
Figure 5 - Painting a Circle

The PAINT statement is used to fill in an area with a solid color until another color is encountered. In the program above, this line:

PAINT (319, 239), 10, 2

is instructing the computer to start filling in a color at coordinate 319, 239 (the center of the circle), with the color bright green (10) until the color dark green (2) is encountered. Since the circle was drawn with a dark green color paint will stay within the boundaries of the circle. Since the portion of the red box inside the circle is not dark green the PAINT statement will ignore it and paint right over top of it. Think of the PAINT statement as a bucket of color being dumped at a certain coordinate and then the paint spreading out over an area. The paint will continue to spread until it encounters the border color specified to stop at. In fact, the algorithm the PAINT statement uses (called a flood-fill) acts exactly in this manner.

Because of the way the PAINT statement works it's important to remember which order objects need to be drawn in. Let's take the previous example and simply swap the LINE and CIRCLE statements, so the circle is drawn first and then the box to see what happens.


SCREEN 12

CIRCLE (319, 239), 100, 2
LINE (50, 50)-(300, 300), 12, BF
PAINT (319, 239), 10, 2

Nasty
Figure 6 - Well that didn't work as planned

Because the box was drawn over top of the circle the PAINT statement's paint was able to spead out over the entire screen only stopping when it saw dark green pixels which is what was remaining of the CIRCLE statement's circle. This same result would have happened even if the box was not filled in by the LINE statement:

Paint Holes
Figure 7 - Holes left over by the box

The box would still have left holes in the circle allowing the paint to leak out all over the screen causing the result you see in Figure 7 above. By planning out your PAINT statement's colors beforehand you can create some pretty impressive results as the example program below illustrates.

'--------------------------------
'- Variable Declaration Section -
'--------------------------------

DIM BorderColor% ' circles drawn using this color
DIM PaintColor% '  a random color to paint the circle
DIM Radius% '      the current circle's radius
DIM x%, y% '       the coordinates of the current circle
DIM Xdir% '        the horizontal direction of the circle
DIM Ydir% '        the vertical direction of the circle
DIM Rdir% '        add/subtract to size of circle radius

'----------------------------
'- Main Program Begins Here -
'----------------------------

SCREEN 12 '                                          enter a graphics screen
RANDOMIZE TIMER '                                    seed random number generator
x% = 319 '                                           start circles in center of screen
y% = 239
DO '                                                 start a loop
    Xdir% = INT(RND(1) * 2) - INT(RND(1) * 2) '      random number from -1 to 1
    Ydir% = INT(RND(1) * 2) - INT(RND(1) * 2) '      random number from -1 to 1
LOOP UNTIL Xdir% <> 0 AND Ydir% <> 0 '               end loop when neither is 0
Radius% = 100 '                                      start with radius of 100
BorderColor% = 8 '                                   circles always drawn this color
Rdir% = -1 '                                         radius will begin shrinking

DO '                                                 main loop starts here
    DO '                                             start a loop
        PaintColor% = INT(RND(1) * 15) + 1 '         random number from 1 to 15
    LOOP UNTIL PaintColor% <> BorderColor% '         leave loop if not border color
    CIRCLE (x%, y%), Radius%, BorderColor% '         draw the initial circle
    PAINT (x%, y%), PaintColor%, BorderColor% '      paint the circle in
    CIRCLE (x%, y%), Radius%, PaintColor% '          draw outside of circle same color
    x% = x% + Xdir% '                                move the x coordinate
    y% = y% + Ydir% '                                move the y coordinate
    Radius% = Radius% + Rdir% '                      increase/decrease radius
    IF (x% < Radius%) OR (x% > 639 - Radius%) THEN ' x hit side of screen?
        Xdir% = -Xdir% '                             yes, reverse x direction
    END IF
    IF (y% < Radius%) OR (y% > 479 - Radius%) THEN ' y hit side of screen?
        Ydir% = -Ydir% '                             yes, reverse y direction
    END IF
    IF (Radius% = 5) OR (Radius% = 100) THEN '       radius too big or small?
        Rdir% = -Rdir% '                             yes, reverse radius adder
    END IF
    _DISPLAY '                                       update screen with results
LOOP UNTIL INKEY$ <> "" '                            loop until user presses key
SYSTEM '                                             return to Windows

Freaky
Figure 8 - The psychedelic 60's are back!

In this example program every circle is drawn with the color of dark gray (8) and then a random paint color other than dark gray is used to fill in the circle. Then the circle is once again drawn with the random color chosen to get rid of the dark gray border around it, making it appear as a solid circle. By creating the circle with a border color that is never used as a paint color it ensures the circle will get filled in no matter what color is already inside the circle that was drawn. No color inside the circle could possibly be the border color because it is never used to paint a circle in therefore the PAINT statement fills the entire circle in, covering any other colors that may lie inside. Go ahead and save this example program as PAINT.bas before continuing on. Once you have saved the example program go ahead and play around with some of the values to see what other interesting results can be made.

-- Your Turn --

Recreate the following screen using the graphics commands you learned from this task:

Old Glory
Figure 9 - Old Glory

The following information will help you create the flag seen in Figure 9 above:

- Each red and white stripe is exactly 36 pixels high
- The blue banner is 310x252 pixels (don't forget coordinate 0, 0 is the starting point)
- The upper left hand circle (star) is located at coordinate 30, 25
- The first circle (star) in the second row is located at 55, 50
- All circles (stars) are spaced evenly horizontally and vertically 50 pixels apart
- Look for patterns in the picture above to reduce the size of your code using controlled loops
- 28 lines of code were created (not counting REM statements) to produce the flag seen in Figure 9
- Try to create the flag in as few lines of code as possible (can you do it in less than 28?!)


Save your project as FLAG.bas when finished.

BONUS!

Instead of circles, create actual stars like so!

Stars!
Figure 10 - Bonus for true stars!


-- COMMAND REFERENCE --


Commands learned in previous tasks:

PRINT
INPUT
DIM
REM or '
CONST
TIME$
VAL()
IF...THEN
AND
END
GOTO
SELECT CASE...END SELECT
CASE
TO
IS
CASE ELSE
colon ( : )
FOR...NEXT
STEP
CLS
SLEEP
_DELAY
SYSTEM
DO...LOOP UNTIL (and variations)
WHILE...WEND

New commands introduced in this task:

SCREEN
LINE
CIRCLE
PAINT
PSET

Concepts learned in previous tasks:

execute
statement
expression
literal string
syntax error
variables
type declarations
literal strings
concatenation
integers
long integers
single precision
double precision
strings
null strings
reserved words
operators ( +, -, *, / )
declare
constant
relational operators
functions
nesting
order of operations
Boolean
conditions
indenting
loops
labels
frame rate
controlled loops
conditional loops

New concepts introduced in this task:

Monochrome
Grayscale
pixels
bits per pixel
radian
counter-clockwise
aspect ratio
algorithm