Menu

CIRCLE Command go GLCD

MBB
2014-04-04
2014-04-12
  • MBB

    MBB - 2014-04-04

    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
  • MBB

    MBB - 2014-04-06

    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?

     
  • Anobium

    Anobium - 2014-04-07

    @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.

     
  • David Stephenson

    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.

     
  • Anobium

    Anobium - 2014-04-07

    @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?

     
  • David Stephenson

    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
    ~~~~~~~~~~~~~

     
  • kent_twt4

    kent_twt4 - 2014-04-07

    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.

     
  • Anobium

    Anobium - 2014-04-07

    @Kent.... Go on. :-) Post the code. :-)

     
  • Chuck Hellebuyck

    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.

     
  • kent_twt4

    kent_twt4 - 2014-04-08

    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.

    'Chip model
    #chip 16f1783,8
    '#include <GLCD.h>
    #include <GLCD_SwapCS.h>
    #define GLCD_SwapCS
    
    #define GLCD_RW  PortA.1    '‘Read/Write pin connection
    #define GLCD_RESET  PortA.5    '‘Reset pin connection
    #define GLCD_CS1  PortA.3    '‘CS1 pin connection
    #define GLCD_CS2  PortA.4    '‘CS2 pin connection
    #define GLCD_RS  PortA.0        '‘RS pin connection
    #define GLCD_ENABLE  PortA.2    '‘Enable pin Connection
    #define GLCD_DB0  PortC.0    '‘Data pin 0 Connection
    #define GLCD_DB1  PortC.1    '‘Data pin 1 Connection
    #define GLCD_DB2  PortC.2    '‘Data pin 2 Connection
    #define GLCD_DB3  PortC.3    '‘Data pin 3 Connection
    #define GLCD_DB4  PortC.4    '‘Data pin 4 Connection
    #define GLCD_DB5  PortC.5    '‘Data pin 5 Connection
    #define GLCD_DB6  PortC.6    '‘Data pin 6 Connection
    #define GLCD_DB7  PortC.7     '‘Data pin 7 Connection
    
    dir PortA out
    dir PortB out
    dim abscissa, ordinate, radiusErr, xoffset, yoffset as Integer
    
    Start:
    GLCDCLS
    wait 100 ms
    line 0,1,127,1                'Draw Line using line command
    line 0,2,127,2
    wait 1 s
    
    GLCDCLS
    wait 100 ms
    
    xoffset = 62
    yoffset = 32
    ordinate = 0
    abscissa = 10     'radius
    radiusErr = 1 - abscissa
    Do While abscissa >=  ordinate
       Pset ((abscissa + xoffset), (ordinate + yoffset), on)
       Pset ((ordinate + xoffset), (abscissa + yoffset), on)
       Pset ((-abscissa + xoffset), (ordinate + yoffset), on)
       Pset ((-ordinate + xoffset), (abscissa + yoffset), on)
       Pset ((-abscissa + xoffset), (-ordinate + yoffset), on)
       Pset ((-ordinate + xoffset), (-abscissa + yoffset), on)
       Pset ((abscissa + xoffset), (-ordinate + yoffset), on)
       Pset ((ordinate + xoffset), (-abscissa + yoffset), on)
       ordinate ++
       If radiusErr < 0 Then
          radiusErr = radiusErr + (2 * ordinate)+ 1
       else
          abscissa --
          radiusErr = radiusErr + 2 * (ordinate - abscissa + 1)
    
       end if
    Loop
    wait 5 s
    goto start
    

    EDIT: moved parenthesis on radiusErr calc, to be consistent with Wikipedia example. Still no difference.

     

    Last edit: kent_twt4 2014-04-08
  • Anonymous

    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)

     
  • Anonymous

    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.

     
  • MBB

    MBB - 2014-04-12

    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.

     

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.