Circles, High Resolution Graphics, and Remedial Math

It was a simple question, really: “How do you draw a circle on the screen?”

Some programming languages have a nice set of graphic primitives that let you specify the coordinates and radius and draw a circle with a single statement… Heck, even the Commodore Plus 4 supported this.  Microsoft BASIC on the Macintosh did.  I even vaguely remember something in the current version of Visual Studio that lets you do this without too much trouble.

Sadly, Applesoft does not and the young programmer in question was working on an ancient Apple IIe that was already considered “Vintage” when he was born.

I’m told there are ways of generating the Cartesian Coordinates of points on a circle without using the Sine or Cosine function… Some other genius can show you this on their own time, but I got wrapped up in essentially converting polar coordinates to Cartesian and Applesoft conveniently DOES have the Sine and Cosine functions readily accessible, even if they are somewhat slow.  The meat of the program are these two functions:

x = r * cos (theta)

y = r * sin (theta)

Where x and y are the Cartesian coordinates of points on the circle, arc, or curve; r is the radius from the central point; and theta is the angle from the horizontal.

Complicating this somewhat, for someone who was taught to think about angular measure in degrees is the fact that Applesoft BASIC uses radians.  We convert degrees to radians by multiplying the angle in degrees by the value of pi divided by 180:

radians = degrees * (PI / 180)

We also need to consider that the screen coordinates place the origin in the upper left-hand corner of the screen and are all positive integers.  Since our functions for X and Y are going to give us negative values, we have to redefine the origin as the middle of the screen:

Cx = INT (280 / 2)

Cy = INT (192 / 2)

Where Cx is the center of the screen along the X axis (Horizontal) and Cy is the center of the screen along the Y axis (Vertical.) By adding the X and Y coordinates generated by our earlier function t0 Cx and Cy, we can plot even those points where the functions give us negative numbers.

So, thus far we can convert Radians to Degrees, convert Polar coordinates to Cartesian, and map those “absolute” coordinates to our actual screen.  The next part of the procedure is simply to step through all “360 Degrees” of the circle and plot those points that lie on the circle. The sample code looks like this:

 1000 TEXT : HOME : SPEED= 255
 1010 HGR2 : HCOLOR= 3
 1020 LET PI = 22 / 7
 1021 LET CX = INT (280 / 2)
 1022 LET CY = INT (192 / 2)
 1030 DEF FNR(D) = D * (PI / 180)
 1050 DEF FNX(D) = R * COS(FNR(D)) + CX
 1060 DEF FNY(D) = R * SIN(FNR(D)) + CY
 1070 LET R = 90
 1080 FOR T = 0 TO 360
 1090 HPLOT FNX(T), FNY(T)
 1100 NEXT T
 1110 END 

And generates the following display on the Apple Iie emulator:


A line-by-line examination of the program follows:

1000 – Sets text mode, clears the screen, and sets the output speed to maximum. Since we don’t know any of these states when we begin execution, this is just precautionary.

1010 – Sets the full-screen high resolution graphics mode and sets the output color to white.

1020 – This defines our constant pi using the rough approximation I learned back in Junior High.

1021 – This defines constant Cx as the center of the screen along the X-Axis.

1022 – This defines constant Cy as the center of the screen along the Y-Axis.

1030 – This user-defined function converts an angle measured in degrees (D) to an angle measured in Radians (R)

1050 – This user-defined function finds the X-coordinate of each point given the angle and radius.

1060 – This user-defined function finds the Y-coordinate of each point given the angle and radius.

1070 – This defines our radius (R) as 90 pixels.

1080 – This marks the beginning of a FOR loop that will step through all 360 degrees of angle T (Theta) that define the circle.  If we wanted to draw an arc instead of a circle, we could limit the range of T to the beginning and end of the arc.

1090 – This plots each point that lies on the circle, as computed by the user-defined functions above.

1100 – This line completes the FOR/NEXT control structure.

1110 – This line marks the end of the program and terminates execution.

Yes, the program works but it’s rather slow and it turns out that we can speed-up execution a bit by treating our circle as a polygon with n equal sides. We accomplish this by adding a STEP value to our FOR/NEXT loop and “remembering” the previous point so that we can draw a straight line between them.

Edit the program as follows:

 1075 LET OX = FNX(O):OY = FNY(0)
 1080 FOR T = 15 TO 360 STEP 15
 1096 LET OX = FN X(T):OY = FNY(T)

Line 1075 finds the first X and Y values at T=0 and stores those values in variables Ox and Oy.

Line 1080 is edited to reflect that we’re now starting at T=15 and working towards 360 in 15 degree steps.

Line 1090 is edited to now draw a line from the “old” coordinates specified by OX,OY and the “current” coordinates found by FNX(T) and FNY(T).

Line 1096 is added to update the values of OX and OY to the “current” values and storing them for the next iteration of the loop.

Drawing the circle in 15 degree steps makes the outline of the circle a little rougher, but the program only loops 23 times versus 360 times in the previous version.  Here’s a sample of the output generated by the revised program:


Although the circle shown here is a little “rough”, this is actually a worst-case scenario since this is probably the largest circle you can practically draw on the Apple II screen — smaller circles will show less irregularity due to the jagged plot lines.

Good Night, All!