GRA+PIX: A System for Pixel Graphics

See all articles from SYNC v1 n5

The following article describes a set of subroutines for drawing lines, arcs, regular geometric shapes, and sectors on the Sinclair ZX80 with the 8K Basic ROM and 16K RAM. The routines should also work on a ZX81 with 16K RAM. In addition, in SLOW mode on the ZX81, you can watch the figures develop as they are drawn, although for some shapes, such as “filled” circles, this will be a very slow process as many points must be calculated before they can be plotted on the display.

The routines presented are useful for displaying data pictorially as pie charts, histograms, and line charts. They are not suitable for animation, so no attempt has been made to optimize them for speed. Also it is left to the reader to provide labeling and titling routines. This is a chore that is best tailored to the individual application, and it should not be difficult to do.

The routines require a full D-FILE to be effective, thus smaller memory systems could not use the package as is. Those users with 8K of RAM (see SYNTAX ZX80, March 1981) should be able to use the entire package. Those with 4K of RAM will have to delete the driver (lines 1 through 255) and the fill routine (lines 9750 through 9895) to fit memory and supply the required parameters by assigning the variables directly before calling the GRA+PIX subroutines. You will have to experiment to see how useful this is.

The term graphic means literally to form by writing, drawing, or engraving—a product of graphic art. Graphics is the art of representing an object on a two-dimensional surface by means of some mathematical rules of projection.

The least rigorous interpretation of a graphic representation is then as simple as a list of words or numbers on a piece of paper set there to convey some meaning to the intended audience. Thus all computer programming languages have some degree of graphic capability. They are all capable of displaying information, either on a hardcopy device, such as a printer or terminal, on a display surface, such as a CRT, or in some other fashion so that the recipient of the information is able to understand what the computer and the program did for him.

With the advent of Basic and the various time-sharing and microcomputer Basic implementations, computer capability has been put within reach of large numbers of people. As these people have become familiar with using the computer, they have demanded and received enhancements in the language. One significant area is the graphic capability of the Basic language. Text graphics is no longer sufficient. With the ability to create moving displays and the capability to form characters by merely setting an electron beam on or off, it became desirable to create picture graphics.

The various microcomputers offer similar ways to do this. With commands such as SET, PLOT, and DRAW, the user is given the ability to turn on or off picture elements (pixels) on his video display in any order by specifying some coordinate point, x-y, and stating whether the point is to be turned on or turned off. Once could laboriously construct any image he wished (within the resolution of the screen) by turning on all the pixels he needed to make the desired image.

However, this can be quite tedious and the novelty of using graphics would quickly vanish if there were not a better way. Fortunately, there is an easier method for many applications of picture graphics. Consider drawing a straight line from the point x=10, y=10 to the point x=50, y=50. One way to do this would be the following simple program.

10 LET Y=10
20 FOR X=10 TO 50
30 PLOT X,Y
40 NEXT X

The result of this program is a straight line from (10,10) TO (50,10). We could change the vertical displacement of the line by changing line 10:

10 LET Y=20

and we would now have a straight line from (10,20) to (50,20). We could even ask for the value of y to be input:

10 PRINT "INPUT Y"
11 INPUT Y

and we could draw horizontal lines all over the screen. So far not very exciting, but it gets better. In a similar fashion we could give a single value of x, allowing y to vary and PLOT vertical straight lines. But how about lines at any angle, from any point (X0,Y0) to any other point (X1,Y1)?

Let us throw away the simple straight line program and brush up a bit on our high school geometry. Look at your Basic manual in the section describing PLOT and UNPLOT. Note how the screen is divided for these commands. The screen consists of small cells, or pixels, in the horizontal (x) and vertical (y) directions, counting from x=0 to x=63 and y=0 to y=43, with the origin at (0,0) in the lower left-hand corner of the screen. This looks just like the graph paper we used to use in high school.

Remember quadrants? If we look at the graph paper, or screen in this case, and if we take a point in the center, say (x=30,y=20), and draw a vertical and horizontal axis line through that point, we would have the screen divided up into four sections, or quadrants. The upper right quadrant is quadrant I, the upper left quadrant II, the lower left quadrant III, and the lower right quadrant IV. See Figure 1.

Figure 1. Screen divided into four quadrants about the point 30,20 (corners 30,43; 0,20; 63,20; 30,0). The lower left quadrant is labeled "Quadrant II" in the printed figure.
Figure 1. Screen divided into four quadrants about the point 30,20 (corners 30,43; 0,20; 63,20; 30,0). The lower left quadrant is labeled "Quadrant II" in the printed figure.

Now if we draw a straight line from the origin of this graph paper, (30,20), to another x-y point, (50,20) we have a horizontal line lying on the x-axis of the first quadrant. Suppose we draw the line from (30,20) to (30,40). We have a straight vertical line lying on the y-axis between the first and second quadrant, at 90 degrees (one quarter of a circle) to the first line. Now draw a line from (30,20) to (50,40). This is still a straight line, but now it lies half way between and x and y axes, at a 45 degree angle. Without getting too rigorous (or boring) in our mathematics, we can develop a relationship between the line we want to draw and the x and y coordinate points using a little geometry.

Draw a triangle with the base a straight line from (30,20) to (50,20). Draw a vertical line from (50,20) to (50,40). Now connect the top of the vertical line (50,40) to the origin (30,20), and we have our triangle. The diagonal line is the one we want to express geometrically. See Figure 2.

Figure 2. Triangle from 30,20 to 50,20 to 50,40 with included angle A.
Figure 2. Triangle from 30,20 to 50,20 to 50,40 with included angle A.

Between the lines (30,20) to (50,20) and (30,20) to (50,40) is an included angle. Let us call it angle A. We can solve for the value of this angle (forget that we already know it is a 45 degrees or PI/4 radians) by using the relations:

SIN A = Opposite side / Hypotenuse
      = (40-20)/SQR((40-20)**2+(50-30)**2)
      = 20/SQR(800) = 0.707106....

The angle whose SIN (ASN) is 0.707106….. is 45 degress or PI/4, which we already knew (in this case). The equation for SIN A can be generalized:

SIN A = (Y1-Y0)/SQR((Y1-Y0)**2+(X1-X0)**2)

A = ASN((Y1-Y0)/SQR((Y1-Y0)**2+(X1-X0)**2))

Now that we have A, so what? Well, for one thing we can now solve for the line we want to draw from (30,20) to (50,40) in this case, or better, from (X0,Y0) to (X1,Y1) in general. Using geometry the line we want (the hypotenuse of our triangle) is related to the tangent of the angle A we just calculated. The tangent is merely the ratio of the SIN to the COS, TAN A = SIN A/COS A, and can be used in the following manner.

For values of x from X0 to X1, in steps of 1, the value of y is calculated as:

DY = SGN (Y1-Y0)*TAN A
Ycurrent = Yprevious + DY

Thus, treating x as the independent variable we are able to calculate the value of each point along the desired line geometrically. Listing 1 is a routine for drawing a straight line from any point (X0,Y0) to (X1,Y1). The routine takes into account that you may have division by zero possibilities (drawing a point) and optimizes the line for the fit. The quadrant is taken into account. The missing lines will be supplied later. The variable P is the PLOT/UNPLOT control. If P=0, the routine will UNPLOT.

For each routine the SYNCSUM is supplied so you can check your entry as you go (see SYNC 1:4, pp. 6-7). Begin by entering the LINE routine in a NEW workspace. Enter the lines as they are listed, making the line deletions when asked to do so. Each SYNCSUM is obtained by examining the contents of memory at the time, so if you want to check your SYNCSUM with that on the listing, it is important that lines be entered in sequence. Also, do not use tokens in REM statements to save space but spell words out exactly as listed so the correct SYNCSUM is obtained.

Cartoon: "HEAPSORT ALGORITHM TOURNAMENT TODAY 11:00 AM."
Cartoon: "HEAPSORT ALGORITHM TOURNAMENT TODAY 11:00 AM."

Listing 1.

9000 REM LINE (X0,Y0,X1,Y1,P)
9015 IF X0<>X1 OR Y0<>Y1 THEN GO
TO 9040
9020 LET D=2
9025 LET DX=0
9030 LET DY=0
9035 GOTO 9105
9040 REM CHECK SLOPE OF LINE
9045 LET A=ABS ((Y1-Y0)/SQR ((Y1
-Y0)*(Y1-Y0)+(X1-X0)*(X1-X0)))
9050 LET A=ASN (A+(1-A)*(A>1))
9055 IF A>PI/4 THEN GOTO 9085
9060 LET D=2*(ABS (X1-X0)+1)
9065 LET DX=SGN (X1-X0)
9070 LET DY=SGN (Y1-Y0)*TAN A
9075 IF ABS DY<1E-7 THEN LET DY=
0
9080 GOTO 9105
9085 LET D=2*(ABS (Y1-Y0)+1)
9090 LET DX=SGN (X1-X0)*COS A/SI
N A
9095 IF ABS DX<1E-7 THEN LET DX=
0
9100 LET DY=SGN (Y1-Y0)
9105 LET X2=X0-DX
9110 LET Y2=Y0-DY
9120 FOR I=1 TO D/2
9125 LET X2=X2+DX
9130 LET Y2=Y2+DY
9135 IF X2<L1 OR X2>U1 THEN GOTO
9155
9140 IF Y2<L2 OR Y2>U2 THEN GOTO
9155
9145 IF NOT P THEN PLOT X2,Y2
9150 IF P THEN UNPLOT X2,Y2
9155 REM HOLD PLACE FOR NOW
9170 NEXT I
9175 RETURN

SYNCSUM = 40

Lines 9135 and 9140 perform a useful function. Normally the full screen will be used for plotting, but on some occasions you may wish to restrict some area for text only, without any possibility of an intrusion from a line calculation. If the variables L1,L2,U1,U2 are set to 0,0,63,43 then the full screen will be used for plots. L1,U1 are the lower and upper limits of the x-axis; and L2, U2, the lower and upper limits of the y-axis. If you would like to restrict the area for plotting to the right half of the screen for example, then set L1=31, U1=63, L2=0, U2=43. If this is done, then any point culculated outside the right half of the screen will not plot. The points will still be calculated, however.

The switch from TAN A to COT A (COS A/SIN A) at PI/4 is done to avoid the division by zero for situations where you have vertical or near vertical lines. The signum (SGN) function is used to adjust to the proper quadrant.

At this point you may ask why such overkill for drawing a simple line? You would be right—a line can be calculated in somewhat simpler fashion. A great deal of care is being taken with this, however, as the LINE routine will be used as a basis for plotting many geometric shapes, such as arcs, polygons, and segments. It will also be used to fill solid objects, so exact boundary conditions must be maintained. For now let us review what is needed to use the routine just presented. Listing 2 will serve as a driver program:

Listing 2.

 1 LET L1=0
2 LET L2=0
3 LET U1=63
4 LET U2=43
5 LET P=0
10 CLS
15 PRINT "INPUT X0,Y0: ";
20 INPUT X0
25 PRINT X0;
30 INPUT Y0
35 PRINT " ";Y0
40 PRINT "INPUT X1,Y1: ";
45 INPUT X1
50 PRINT X1;
55 INPUT Y1
60 PRINT " ";Y1
65 PRINT "HIT N/L TO CONTINUE"
70 INPUT A$
75 CLS
80 GOSUB 9000
85 INPUT A$
90 GOTO 10

In Listing 2 you are prompted for the starting point, X0-Y0, and the ending point, X1-Y1. You could also have requested whether or not you would like to change the screen limits, set by lines 1-4.

Suppose instead of drawing a line from X0-Y0 to wherever we instead used X0-Y0 as the center of some shape we want to draw. Take for example a circle. If we let X0-Y0 be the center and call the hypotenuse of the previous discussion the radius of the circle, then we should be able to calculate a whole series of X1-Y1 points lying on the perimeter of the circle by varying the angle A from 0 radians (or degrees) to 2*PI radians (360 degrees). If we then connected these points with straight lines we could approximate the shape of a circle. The more points we calculated, the better the circle would look.

Call the origin point 01 and 02 rather than X0, Y0 to avoid ambiguities. Let our convention be to draw the circle starting with A=0 radians, counter-clockwise through quadrants I – IV, until A=2*PI radians. The starting x,y point would then be:

X0=01+R
Y0=02

It can be shown that the x coordinate varies with the COS A and the y coordinate varies with the SIN A:

X1=01+R*COS A
Y1=02+R*SIN A

where R is the length of the radius (hypotenuse). Furthermore if we divide the circle up into N sections, or segments, then we can work around the perimeter of the circle in multiples of A radians:

1 <= i <=N
A = 2*PI/N

then

Xi=01+R*COS(i*A)
Yi=02+R*SIN(i*A)

Now we can develop a program to draw circles using the line program as a subroutine. Delete lines 1 to 90 of the Listing 2 driver program to get a good SYNCSUM. Listing 3 is a circle example.

Listing 3.

9300 REM CIRCLE (O1,O2,R,N,P)
9305 LET A1=2*PI/N
9310 LET X0=O1+R
9315 LET Y0=O2
9320 FOR J=1 TO N
9325 LET X1=O1+R*COS (J*A1)
9330 LET Y1=O2+R*SIN (J*A1)
9335 GOSUB 9000
9340 LET X0=X1
9345 LET Y0=Y1
9350 NEXT J
9355 RETURN
SYNCSUM = 32

A simple driver program to use the circle program is found in Listing 4.

Listing 4.

 1 LET L1=0
2 LET L2=0
3 LET U1=63
4 LET U2=43
5 LET P=0
10 CLS
15 PRINT "ENTER ORIGIN: O1,O2:
"
20 INPUT O1
25 INPUT O2
30 PRINT "ENTER RADIUS: R"
35 INPUT R
40 PRINT "NO. SEGMENTS/CIRCLE:
N"
45 INPUT N
50 CLS
55 GOSUB 9300
60 INPUT A$
65 GOTO 10
SYNCSUM = 134

Try playing with this function for a while. RUN it and see what happens as N is varied from 1 and 2 and larger. Notice that all the figures you see plotted are circles. Since the PLOT pixels are relatively coarse, lines at any angle other than vertical or horizontal are rather ragged, and some peculiar things may happen because of this. Ignoring this, you should be seeing what look like triangles, squares, pentagons, and so on until N is large enough that the polygon begins to look like a circle. In fact, it is never a circle but always a polygon due to the coarseness of the pixels, but it does approximate a circle.

Instead of using an angle A let us break the angle up into two angles, PHI (P2) and THETA (T2) and generalize the circle function a bit. If we take a polygon and draw it on our graph paper, we could rotate it counter-clockwise on its origin at any angle from 0 to 2*PI radians. After 2*PI radians the cycle repeats. We will call this angle the inclination angle, PHI (P2).

The included angle can also vary from 0 to 2*PI radians. If the angle is 2*PI radians, then the object is enclosed and we shall call it a polygon—a “generalized circle.” If the angle is less than 2*PI radians, then the object is not closed and we have an arc. Call the angle THETA (T2). A generalized routine is shown in Listing 5. There are some calculations that may not seem necessary, but they will be used later. Delete the Listing 4 driver program lines 1 to 65 to ensure a good SYNCSUM. The polygon routine will overlay the previous circle program if entered correctly, so these lines need not be expressly deleted.

Listing 5.

9300 REM POLYGON/ARC (O1,O2,R,N,
T2,P2,P)
9305 LET F=1
9310 GOSUB 9900
9315 REM ENTER FROM SEGMENT
9320 LET X0=INT (P5+O1+R*COS P2)
9325 LET Y0=INT (P5+O1+R*SIN P2)
9330 FOR J=1 TO N1
9335 IF F THEN GOTO 9345
9340 IF D2>=D1-1 THEN GOTO 9415
9345 LET T4=J*T1
9350 IF ABS (T2-T4)<=0.01 THEN L
ET T4=T2
9355 IF J=N1 AND T3=2*PI THEN LE
T T4=T3
9360 LET X1=INT (P5+O1+R*COS (P2
+T4))
9365 LET Y1=INT (P5+O2+R*SIN (P2
+T4))
9370 LET X3=X1
9375 LET Y3=Y1
9380 GOSUB 9010
9385 IF NOT F THEN GOSUB 9750
9390 LET X0=X3
9395 LET Y0=Y3
9400 IF T2=T4 THEN GOTO 9410
9405 NEXT J
9410 IF F THEN GOSUB 9970
9415 RETURN

9900 REM UTILITY
9905 LET T1=(2*PI)/N
9910 LET P2=ABS P2
9915 LET T2=ABS T2
9920 IF P2>2*PI THEN LET P2= [illegible] -2
*PI*INT (P2/(2*PI))
9925 IF T2>2*PI THEN LET T2= [illegible] -2
*PI*INT (T2/(2*PI))
9930 LET L0=1
9935 LET P3=P2
9940 LET T3=T2
9945 LET P5=0.5
9950 LET N0=INT (P5+T2/T1)
9955 LET N1=N0
9960 LET T2=N0*T1
9965 RETURN
9970 LET P2=P3
9975 LET T2=T3
9980 DIM X(1)
9985 DIM Y(1)
9990 DIM Z(1)
9995 RETURN
SYNCSUM = 231

Be certain to include all the lines above, as they will be used. Lines 9900 on are the start of the utility routines that will be used more extensively. Essentially what we have so far is a line program, a polygon/arc routine, some utility functions, and what appears to be some unused code. In the interest of brevity I will not provide a driver program until all the code is complete. It would be a good idea to SAVE your entries on tape often, in case you have problems, so you have some recovery point. I have found that with the 16K RAM attached tape reliability suffers for long files. So a good quality tape, a tape demagnitizer, frequent head cleaning and demagnitizing, several SAVEs in a row to assure a good SAVE, and lots of patience are needed to make LOADable tape copies.

A segment can be visualized as looking like a slice of pie. It is really nothing more than an arc of less than 2*PI radians with two straight lines from the point of origin connecting each end of the arch. We can easily plot a segment by calling the LINE routine to draw the lines from the origin to the arc end points and the polygon routine to draw the arc itself. The routine to do this follows. The variable, F, will be used later when we fill the segments. For the time being assume F to be 1. Listing 6 shows the SEGMENT routine:

Listing 6.

9500 REM SEGMENT (O1,O2,R,N,T2,P
2,P,F)
9505 GOSUB 9900
9510 IF T2<=PI OR F THEN GOTO 95
35
9515 LET N1=INT (N/2)
9520 LET L0=INT (0.55+N0/N1)
9525 LET N0=N0-N1
9530 LET T2=N1*T1
9535 FOR L=1 TO L0
9540 LET X0=INT (P5+O1)
9545 LET Y0=INT (P5+O2)
9550 LET X1=INT (P5+O1+R*COS P2)
9555 LET Y1=INT (P5+O2+R*SIN P2)
9560 GOSUB 9010
9565 IF F THEN GOTO 9595
9570 LET D0=D
9575 DIM X(D)
9580 FOR J=1 TO D
9585 LET X(J)=Z(J)
9590 NEXT J
9595 LET X1=INT (P5+O1+R*COS (P2
+T2))
9600 LET Y1=INT (P5+O2+R*SIN (P2
+T2))
9605 GOSUB 9010
9610 IF F THEN GOTO 9645
9615 LET D1=D
9620 LET D2=1
9625 DIM Y(D1)
9630 FOR J=1 TO D1
9635 LET Y(J)=Z(J)
9640 NEXT J
9645 GOSUB 9315
9650 IF F THEN GOTO 9715
9655 IF D0=2 THEN GOTO 9695
9660 LET D=D1
9665 DIM Z(D)
9670 FOR J=1 TO D-1 STEP 2
9675 LET Z(J)=Y(D-J)
9680 LET Z(J+1)=Y(D-J+1)
9685 NEXT J
9690 GOSUB 9750
9695 IF N0<N1 THEN LET N1=N0
9700 LET N0=N0-N1
9705 LET P2=P2+T2
9710 LET T2=N1*T1
9715 NEXT L
9720 GOSUB 9970
9725 RETURN
SYNCSUM = 168

A GOSUB to 9500 with F=1 and the other variables set as desired will plot a segment on the screen. The remaining task is to fill the segment (or unfill it) if desired. By fill, I mean to use the outline of the segment drawn and turn on or off all the pixels inside its boundaries. In this way pie charts can be created.

This procedure requires two steps: first to draw the outline of the shape and “remember” it, and second to uniquely turn on each point within the shape. It is a cycle burner to say the least, as you will discover when you run the routine. A filled circle (a segment with the included angle T2 equal to 2*PI radians) with a radius of 20 and number of segments 24 will take somewhat less than 5 minutes in FAST mode! You can enjoy several cups of coffee if you watch it in SLOW mode on a ZX81.

The approach I have used is quite simple, but, still quite time consuming. Using a value of F=0 for fill/unfill, the line routine is triggered to generate a vector of the x-y points it creates. When the return is made to the segment routine, it stores the vector Z in a new vector X or Y, depending on which edge it is. LINE is called again, repeatedly, to generate the vector of arc points. After each call, one segment at a time, each point in the arc vector is connected uniquely to the corresponding point in the X or Y vector by a call to the LINE routine. The line drawn is either a vertical or a horizontal line, from a point in Z to a corresponding point in either vector X or Y as appropriate. If there are not enough points in Z to finish the fill, an adjustment is made. The angle of inclination, PHI (P2), is checked to minimize redundant or non-connecting points.

It should be noted that to make life a little easier, the included angle, THETA (T2), is forced to be an integer multiple of the size of the segment angle, N. You may set it to whatever you wish but it will be adjusted if necessary. This should not be too noticeable given the coarseness of low resolution pixel graphics.

A second note in using the system is this: segments are the only objects that will be filled. If you want to fill a polygon, just call the segment routine with a value for THETA (T2) of 2*PI radians and you can create a filled polygon (or unfill an existing filled polygon). It is that simple. Listing 7 shows the remainder of the subroutine code for GRA+PIX.

Listing 7.

9005 LET F=1
9115 IF NOT F THEN DIM Z(D)
9155 IF F THEN GOTO 9170
9160 LET Z(2*I-1)=INT (X2+P5)
9165 LET Z(2*I)=INT (Y2+P5)

9750 REM FILL ROUTINE
9755 LET F=1
9760 LET P4=1
9765 IF (P2>=0 AND P2<P5*PI) OR
(P2>=PI AND P2<(1+P5)*PI) THEN L
ET P4=0
9770 FOR K=1 TO D-1 STEP 2
9775 LET X0=Z(K)
9780 LET Y0=Z(K+1)
9785 IF P4 THEN GOTO 9805
9790 IF X0<>X1 THEN GOTO 9820
9795 LET Y1=Y0
9800 GOTO 9880
9805 IF Y0<>Y1 THEN GOTO 9820
9810 LET X1=X0
9815 GOTO 9880
9820 IF D0=2 THEN GOTO 9855
9825 IF K<>1 THEN LET D0=D0-2
9830 IF NOT P4 AND X0<>X(D0-1) T
HEN GOTO 9820
9835 IF P4 AND Y0<>X(D0) THEN GO
TO 9820
9840 LET X1=X(D0-1)
9845 LET Y1=X(D0)
9850 GOTO 9880
9855 IF K<>1 THEN LET D2=D2+2
9860 IF NOT P4 AND X0<>Y(D2) THE
N GOTO 9855
9865 IF P4 AND Y0<>Y(D2+1) THEN
GOTO 9855
9870 LET X1=Y(D2)
9875 LET Y1=Y(D2+1)
9880 GOSUB 9010
9885 NEXT K
9890 LET F=0
9895 RETURN
SYNCSUM = 158

Listing 8.

  1 REM GRA+PIX DRIVER MODULE
5 DIM S$(64)
10 LET R1=PI/180
15 LET L1=0
20 LET L2=0
25 LET U1=63
30 LET U2=43
35 LET S=0
40 PRINT AT 20,0;S$
41 PRINT AT 20,0;"CHANGE L1,L2
,U1,U2 FROM: ";L1;" ";L2;" ";U1;
" ";U2;" (Y/N)?"
45 INPUT A$
50 IF A$="N" THEN GOTO 65
55 PRINT AT 20,0;S$
56 PRINT AT 20,0;"ENTER L1,L2,
U1,U2:"
60 INPUT L1
61 INPUT L2
62 INPUT U1
63 INPUT U2
65 PRINT AT 20,0;S$
66 PRINT AT 20,0;"PLOT OR UNPL
OT? (P/U):"
70 INPUT A$
75 IF A$="P" THEN LET P=0
80 IF A$<>"P" THEN LET P=1
85 PRINT AT 20,0;S$
86 PRINT AT 20,0;"LINE,POLYGON
,ARC, OR SEGMENT? (L/P/A/S):"
90 INPUT A$
95 IF A$="L" THEN GOTO 225
100 IF A$="P" THEN GOTO 160
105 IF A$="A" THEN GOTO 135
110 LET S=1
115 PRINT AT 20,0;S$
116 PRINT AT 20,0;"FILL (OR UNF
ILL)? (Y/N):"
120 INPUT A$
125 IF A$="Y" THEN LET F=0
130 IF A$<>"Y" THEN LET F=1
135 PRINT AT 20,0;S$
136 PRINT AT 20,0;"ENTER INCLUD
ED ANGLE (T2) AND ORIENTATION
ANGLE (P2) (DEGREES):"
140 INPUT T2
141 INPUT P2
145 LET P2=P2*R1
150 LET T2=T2*R1
155 GOTO 180
160 LET T2=2*PI
165 PRINT AT 20,0;S$
166 PRINT AT 20,0;"ENTER ORIENT
ATION ANGLE (P2) (DEGREES):"
170 INPUT P2
175 LET P2=P2*R1
180 PRINT AT 20,0;S$
181 PRINT AT 20,0;"ENTER ORIGIN
(O1,O2):"
185 INPUT O1
186 INPUT O2
190 PRINT AT 20,0;S$
191 PRINT AT 20,0;"ENTER RADIUS
(R):"
195 INPUT R
200 PRINT AT 20,0;S$
201 PRINT AT 20,0;"HOW MANY SEG
MENTS/FULL CIRCLE?"
205 INPUT N
210 IF S THEN GOSUB 9500
215 IF NOT S THEN GOSUB 9300
220 GOTO 240
225 PRINT AT 20,0;S$
226 PRINT AT 20,0;"ENTER X0,Y0,
X1,Y1:"
230 INPUT X0
231 INPUT Y0
232 INPUT X1
233 INPUT Y1
235 GOSUB 9000
240 PRINT AT 20,0;S$
241 PRINT AT 20,0;"ANOTHER SET?
(Y/N):"
245 INPUT A$
250 IF A$<>"N" THEN GOTO 35
255 STOP
SYNCSUM = 114

The GRA+PIX routines are normally expected to be used as subroutines to another program, however they can be used from a driver program, calling the routines over and over and building images to gain some experience with how the routines and pixel graphics work. The driver routine in Listing 8 will prompt you for the required parameters, plot the desired figure, and return to you to ask for more input while leaving the image just plotted on the screen. If you do not like the image plotted, just unplot it by responding to the driver prompts. You can in this way build up complex geometric shapes on the screen. Your imagination is the limit, so have fun!

Some of the code in the GRA+PIX program had to be written the way it appears because of some problems in the 8K Basic ROM. I think I have programmed around most of these errors, but there may be some cases I have not discovered.

You need not be limited to low-res graphics. It should be possible to use your own character set to create high resolution graphics characters by creating your own 8×8 bit pattern and calling the video driver from RAM rather than ROM. This is one reason I have elected to use the TAN/COT approach for generating lines rather than the simpler delta x/delta y step method. If time permits, I will attempt to follow up with a method to do this.

I would be interested in hearing comments on the use of GRA+PIX, and would be happy to try to answer any questions if a SASE is enclosed.

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.