I wrote some software that will draw circles on a 128x64 GLCD. The code is below. If it works for other users, you may want to add it to the glcd.h file.
Marty
;Variables
Dim Radius As integer
Dim Radius_2 As integer
Dim x_1 As integer
Dim y_1 As integer
Dim G_1 As integer
Dim y_2 As integer
Dim y_3 As integer
Dim x_2 As integer
'
InitGLCD
GLCDCLS
'
'RADIUS is the radius of the circle
'OffsetX is the left most edge of the circle
'OffsetY is the top most edge of the circle
'
'Some examples using the CIRCLE command
'
Circle 16, 48, 15; Small circle with center in MIDDLE of display
Circle 10, 10, 10; Circle in TOP LEFT of display
Circle 10, 100, 10; Circle in TOP Right of display
Circle 10, 10, 40; Circle in BOTTOM LEFT of display
Circle 10, 100, 40; Circle in BOTTOM Right of display
Circle 32, 32, 0; Large circle with center in MIDDLE of display
'
'SUB ROUTINES ********************************************
'
Sub Circle (In Radius, In OffsetX, In OffsetY)
'For a 128x64 GLCD -
' If 2xRadius plus OffsetX > 127, circle will move off right edge of screen
' If 2xRadius plus OffsetY > 63, circle will move off bottom edge of screen
' The below two IF statements address this situation by reducing OffsetX and/or OffsetY
'
If (2 * Radius + OffsetX) > 127 then
OffsetX = 127 - (2 * Radius)
end If
'
If (2 * Radius + OffsetY) > 63 then
OffsetY = 63 - (2 * Radius)
end If
'
Radius_2= Radius * (-1); Negative Radius numbers for plotting other side of circle
For x_1 = Radius_2 to Radius
y_1 = (Radius * Radius) - (x_1 * x_1); y-axis squared
'
Sq_Root y_1, G_1
'
x_2 = x_1 + Radius + OffSetX; x-axis
y_2 = G_1 + Radius + OffSetY; y-axis for bottom half of circle
y_3 = abs(G_1 - Radius) + OffSetY; y-axis for top half of circle
'
pset x_2,y_2,on; Bottom half of circle
pset x_2,y_3,on; Top half of circle
'
next
'
End Sub
Sub Sq_root (In y_1, G_1)
'SQUARE ROOT Approximation &&&&&&&&&&&&&
'
G_1 = 200;First Guess for 5 Digit y_1
If y_1 < 10000 then
G_1 = 60;First Guess for 4 Digit y_1
End If
If y_1 < 1000 then
G_1 = 20;First Guess for 3 Digit y_1
End If
If y_1 < 100 then
G_1 = 6;First Guess for 2 Digit y_1
End If
If y_1 < 10 then
G_1 = 2;First Guess for 1 Digit y_1
End If
'
G_1 = (G_1 + (y_1 / G_1))/2
G_1 = (G_1 + (y_1 / G_1))/2
G_1 = (G_1 + (y_1 / G_1))/2
G_1 = (G_1 + (y_1 / G_1))/2;Square Root of y_1
End Sub
Edit by Anobium to sort the listing.
Last edit: Anobium 2014-04-07
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I believe the standard procedure for drawing circles on a graphical display is the "bresenham algorithm". It is computationally very fast as only requires simple + and - operations.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I have an example that I wrote in QBasic
~~~~~~~~~~~
DEFINT A-Z
SCREEN 12
y = 200 'this is the radius
xoff = 300: yoff = 200: x = -1 'offsets
WHILE x < y
x = x + 1: d = d + x + x + 1
IF d > 0 THEN d = d - y - y + 1: y = y - 1
FOR k = 0 TO 7
IF k AND 4 THEN a = -x ELSE a = x
IF k AND 2 THEN b = -y ELSE b = y
IF k AND 1 THEN SWAP a, b
PSET (a + xoff, b + yoff)
NEXT k
WEND
~~~~~~~~~~~~~
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I ran your code MBB and it ran fine in the Real Pic Simulator.
I just had to swap the CS1 and CS2 connections as discussed in another post topic.
This is a great example. There were a few missing pixels in the circles for some reason.
I'll try this on my actual KS0108 GLCD in the future but so far the Simulator and the real display have matched. I just have to swap the CS1 and CS2 connections.
I tried porting David Stephenson's code and at best could get one good quadrant and one malformed half circle. I am sure the shorthand would work if properly implemented :-).
So, on to Wikipedia, say what? ya I know. So here is a longer hand form. There is a couple of extra pixels pushed into the positive and negative upper quadrants a few pixels above the x axis. Also, there always seems to be a stray pixel about. Could be a bug in GLCD working with integers, or??
No work has been done on offsets, it is very sensitive to that, and haven't gotten in to that.
EDIT: moved parenthesis on radiusErr calc, to be consistent with Wikipedia example. Still no difference.
Last edit: kent_twt4 2014-04-08
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Anonymous
-
2014-04-08
It seems to me that a more compact alternative would be to draw the circle parametrically, since we now have sine and cosine. Let R be the radius, and let T go from 0 to 359. Then,
X = RCos(T) and Y= RSin(T)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Anonymous
-
2014-04-11
I think MBB has a good approach for the circle, though I do want to compare it with the parametric tack just for giggles.
Anyway, it got me thinking about square roots. His code uses Newton's method, with four iterations. That's an excellent method for humans, but since the PIC is doing integer arithmetic, there is no rounding and the truncations accumulate. I put together an Open Office spreadsheet to observe this, which I've attached if you're interested. You can change the initial guess and the range of values to inspect.
Notice that error in the algorithm is never more than +/- 1. If you're talking about numbers in the thousands, this is of no consequence. But with small numbers it matters more. Hence, the gaps in the circle close to the X-axis that Kent observed, I believe.
This is certainly not deadly; MBB's approach is very fine.
A possible improvement, though, is to scale everything up at the outset by 10 or even 100 and work in some rounding and then at the end before drawing, scale back down again.
But now we're talking lots of calculations (remember, the original routine already had four iterations of Newton's method). Which brings us back to the parametric approach I suggested earlier. In that case, there is only a single multiplication for the X value and a single one for the Y value. And the errors do not accumulate.
I just got a GLCD and so rather than woolgathering will experiment myself. But I did want to pass on these thoughts beforehand.
And, again, thanks go to MBB for sharing his code.
I agree with Thomas about the integer math being a big problem, but no matter how good we get at improving the precision of our numbers by scaling or some other means there is still the fact that the GLCD plots them in pixels which are integers.
To fill in the gaps in the circles you would need both an x AND y value that are different from the previous and next x AND y values - difficult or impossible to do with pixel integers.
Thomas' other approach using sin and cosine is interesting and should be tried but I think he'll get very similar results to my method. Here's why I say this:
The CIRCLE software I previously posted uses the equation for a circle -
x^2 + y^2 = Radius^2, which is also the formula for right triangle.
Since it is right triangle, sin(T) = x/Radius and Cos(T) = y/Radius which are the same formulas proposed by Thomas, therefore, results should be the same.
Something else to keep in mind is that from 0 to 30 degrees the sine changes from 0 to 0.5 (delta of 0.5) and the cosine changes from 1 to 0.866 (delta of 0.134). The sine is changing rapidly and the cosine is changing slowly, i.e. a small change in y has a large change in x which will cause problems with the integer pixels.
On the other end, from 60 to 90 degrees the sine goes from 0.866 to 1 and the cosine goes from 0.5 to 0. Here a small change in x has a large change in y.
Since the Sin/Cos approach and the x^2 + y^2 = Radius^2 approach probably produce the same results, the small and large x,y vs angle apply to both methods.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I wrote some software that will draw circles on a 128x64 GLCD. The code is below. If it works for other users, you may want to add it to the glcd.h file.
Marty
Edit by Anobium to sort the listing.
Last edit: Anobium 2014-04-07
I should have mentioned that Radius, OffsetX, and OffsetY are measured in pixels.
Also, when I pasted in the above code, two parts of it were put inside gray boxes and the rest was not. Does anyone know why this happens?
@MBB
Thank you! I will add soon. I have GLCD on test and I can adapt the include file.
To sort you listing.... use four tidlers '~~~~' without the quotes at top and bottom of the code and.... indent [in your editor] but one space.
I believe the standard procedure for drawing circles on a graphical display is the "bresenham algorithm". It is computationally very fast as only requires simple + and - operations.
@David. All methods are good, I also saw this as an example of Rads and Sqr Root.
Do you have an GCB example we can publish?
I have an example that I wrote in QBasic
~~~~~~~~~~~
DEFINT A-Z
SCREEN 12
y = 200 'this is the radius
xoff = 300: yoff = 200: x = -1 'offsets
WHILE x < y
x = x + 1: d = d + x + x + 1
IF d > 0 THEN d = d - y - y + 1: y = y - 1
FOR k = 0 TO 7
IF k AND 4 THEN a = -x ELSE a = x
IF k AND 2 THEN b = -y ELSE b = y
IF k AND 1 THEN SWAP a, b
PSET (a + xoff, b + yoff)
NEXT k
WEND
~~~~~~~~~~~~~
I tried the MBB's code, and pretty cool. The circles are not closed about the x-axis with a few missing pixels. The y-axis closure seemed fine.
@Kent.... Go on. :-) Post the code. :-)
I ran your code MBB and it ran fine in the Real Pic Simulator.
I just had to swap the CS1 and CS2 connections as discussed in another post topic.
This is a great example. There were a few missing pixels in the circles for some reason.
I'll try this on my actual KS0108 GLCD in the future but so far the Simulator and the real display have matched. I just have to swap the CS1 and CS2 connections.
I tried porting David Stephenson's code and at best could get one good quadrant and one malformed half circle. I am sure the shorthand would work if properly implemented :-).
So, on to Wikipedia, say what? ya I know. So here is a longer hand form. There is a couple of extra pixels pushed into the positive and negative upper quadrants a few pixels above the x axis. Also, there always seems to be a stray pixel about. Could be a bug in GLCD working with integers, or??
No work has been done on offsets, it is very sensitive to that, and haven't gotten in to that.
EDIT: moved parenthesis on radiusErr calc, to be consistent with Wikipedia example. Still no difference.
Last edit: kent_twt4 2014-04-08
It seems to me that a more compact alternative would be to draw the circle parametrically, since we now have sine and cosine. Let R be the radius, and let T go from 0 to 359. Then,
X = RCos(T) and Y= RSin(T)
I think MBB has a good approach for the circle, though I do want to compare it with the parametric tack just for giggles.
Anyway, it got me thinking about square roots. His code uses Newton's method, with four iterations. That's an excellent method for humans, but since the PIC is doing integer arithmetic, there is no rounding and the truncations accumulate. I put together an Open Office spreadsheet to observe this, which I've attached if you're interested. You can change the initial guess and the range of values to inspect.
Notice that error in the algorithm is never more than +/- 1. If you're talking about numbers in the thousands, this is of no consequence. But with small numbers it matters more. Hence, the gaps in the circle close to the X-axis that Kent observed, I believe.
This is certainly not deadly; MBB's approach is very fine.
A possible improvement, though, is to scale everything up at the outset by 10 or even 100 and work in some rounding and then at the end before drawing, scale back down again.
But now we're talking lots of calculations (remember, the original routine already had four iterations of Newton's method). Which brings us back to the parametric approach I suggested earlier. In that case, there is only a single multiplication for the X value and a single one for the Y value. And the errors do not accumulate.
I just got a GLCD and so rather than woolgathering will experiment myself. But I did want to pass on these thoughts beforehand.
And, again, thanks go to MBB for sharing his code.
I agree with Thomas about the integer math being a big problem, but no matter how good we get at improving the precision of our numbers by scaling or some other means there is still the fact that the GLCD plots them in pixels which are integers.
To fill in the gaps in the circles you would need both an x AND y value that are different from the previous and next x AND y values - difficult or impossible to do with pixel integers.
Thomas' other approach using sin and cosine is interesting and should be tried but I think he'll get very similar results to my method. Here's why I say this:
The CIRCLE software I previously posted uses the equation for a circle -
x^2 + y^2 = Radius^2, which is also the formula for right triangle.
Since it is right triangle, sin(T) = x/Radius and Cos(T) = y/Radius which are the same formulas proposed by Thomas, therefore, results should be the same.
Something else to keep in mind is that from 0 to 30 degrees the sine changes from 0 to 0.5 (delta of 0.5) and the cosine changes from 1 to 0.866 (delta of 0.134). The sine is changing rapidly and the cosine is changing slowly, i.e. a small change in y has a large change in x which will cause problems with the integer pixels.
On the other end, from 60 to 90 degrees the sine goes from 0.866 to 1 and the cosine goes from 0.5 to 0. Here a small change in x has a large change in y.
Since the Sin/Cos approach and the x^2 + y^2 = Radius^2 approach probably produce the same results, the small and large x,y vs angle apply to both methods.