1
\$\begingroup\$

I've written this program that, renders a circle in the middle of the screen. It's been written in a very elementary way.

Any suggestions on principles or techniques? Or any more optimal or efficient ways of drawing circles in C?

I'm using SDL2 (Simple Direct Media Library) and GCC to compile.

#include "SDL.h"
#include <math.h>
#include <stdio.h>

#define PI  3.14159
#define SCR_WDT  1280
#define SCR_HGT  960

const int SCR_CEN_X = SCR_WDT / 2;
const int SCR_CEN_Y = SCR_HGT / 2;

struct Circle
{
    int radius;
    int h; // The X access of the center of the circle.
    int k; // The Y access of the center of the circle.
    int new_x;
    int new_y;
    int old_x;
    int old_y;
    float step; 
};

int main ( int argc, char *argv[] )
{
    SDL_Init ( SDL_INIT_VIDEO ); 
    SDL_Window *window = SDL_CreateWindow ( "Drawing a Circle",       SDL_WINDOWPOS_UNDEFINED, 
                                             SDL_WINDOWPOS_UNDEFINED, SCR_WDT, SCR_HGT, 0 );
    SDL_Renderer *renderer = SDL_CreateRenderer ( window, -1, SDL_RENDERER_SOFTWARE );

    Circle circle;
    circle.radius = 200;
    circle.h = SCR_CEN_X;
    circle.k = SCR_CEN_Y;
    circle.new_x = 0;
    circle.new_y = 0;
    circle.old_x = 0;
    circle.old_y = 0;
    circle.step = (PI * 2) / 50;

    bool is_running = true;
    while ( is_running )
    {
        SDL_Event event;
        if ( SDL_PollEvent( &event ))
        {
            if ( event.type == SDL_QUIT )
            {
                is_running = false;
            }   
        }

        SDL_RenderClear ( renderer );

        SDL_SetRenderDrawColor ( renderer, 255, 255, 255, 255 );

        for ( float theta = 0; theta < (PI * 2); theta += circle.step )
        {
            circle.new_x = circle.h + (circle.radius * cos ( theta ));
            circle.new_y = circle.k - (circle.radius * sin ( theta ));  

            SDL_RenderDrawLine ( renderer, circle.old_x, circle.old_y,
                                 circle.new_x, circle.new_y );

            circle.old_x = circle.new_x;
            circle.old_y = circle.new_y;

        }

        SDL_SetRenderDrawColor ( renderer, 0, 0, 0, 255 );
        SDL_RenderPresent ( renderer ); 
    }

    SDL_Quit ();

return 0; 
}
\$\endgroup\$
4
  • 5
    \$\begingroup\$ c? Or c++? Seems like this should only be tagged C. \$\endgroup\$
    – Zeta
    Oct 21, 2016 at 5:41
  • \$\begingroup\$ @Zeta this is compiled as C++, since Circle circle; compiles without a typedef. But that's all the "++" there is to it indeed. \$\endgroup\$
    – Quentin
    Oct 21, 2016 at 12:24
  • \$\begingroup\$ @Quentin Good point. And there's bool. But to be honest, I didn't read the code, only the description, where only C was stated. \$\endgroup\$
    – Zeta
    Oct 22, 2016 at 13:03
  • \$\begingroup\$ You would do well to read about Bresenham's circle drawing algorithm. \$\endgroup\$ Oct 24, 2016 at 9:16

2 Answers 2

2
\$\begingroup\$

Doesn't SDL have a function for drawing a circle, or are you just doing that as practise?

#define PI  3.14159

math.h should have M_PI.

struct Circle {
    int radius;
    int h; // The X access of the center of the circle.
    int k; // The Y access of the center of the circle.

Just call them x and y, or center_x and center_y.

int new_x; int new_y; int old_x; int old_y; float step; 

These should be in the function that draws the circle, as they're part of the logic of drawing, not the circle as an object.

int main (int argc, char *argv[])
    Circle circle;
    circle.radius = 200;
    circle.h = SCR_CEN_X;

Make a function to initialize the Circle struct, esp. as you have fields that are initialized to as uninteresting a value as zero.

        if ( event.type == SDL_QUIT ) {
            is_running = false;

I would break out of the loop here, since you're going to exit soon anyway.

    for ( float theta = 0; theta < (PI * 2); theta += circle.step ) {
        circle.new_x = circle.h + (circle.radius * cos ( theta ));
        circle.new_y = circle.k - (circle.radius * sin ( theta )); 
        SDL_RenderDrawLine ( renderer, circle.old_x, circle.old_y,
                             circle.new_x, circle.new_y );

Put the circle drawing in a function, and make new_x, new_y etc. locals to it. As for step, you might want to scale it based on the circle's radius. Or make it a parameter to the function.

I think you have a sort of an off-by-one error here, as (1) on the first iteration of the loop, you draw starting from (old_x, old_y), but old_x and old_y aren't set yet. And (2) when the loop ends, you probably want to draw a line from the last point (at angle 49/50*2*pi) to the very first point (at angle 0) to complete the circle.

Also, decide if you're writing C or C++. You can't define a struct Circle as just Circle foo in C, and in C++, you should make the class have a proper constructor.

\$\endgroup\$
3
  • \$\begingroup\$ I don't think SDL has a way to draw circles. None the less, I'm doing this as practice. I want to get better at writing C code and using SDL so I thought doing small projects like this and getting more experienced programmer's input was a good way to stretch my abilities. \$\endgroup\$
    – dserres
    Oct 21, 2016 at 23:11
  • \$\begingroup\$ Thanks for the suggestions. I've gotten rid of the struct all together and placed everything in a DrawCircle function. And thanks for pointing out the off by one error. I knew there was going to be an issue and could reason how I set the old_x and old_y properly until after I had the circle drawn out. I forgot about it and I was surprised that the circle actually worked even with this over sight. I've set it now and the circle is much more complete. \$\endgroup\$
    – dserres
    Oct 21, 2016 at 23:24
  • 1
    \$\begingroup\$ I'm not sure at this hour, but since something like 1/50, let alone pi, cannot be represented exactly as a floating point number, you might get something very little less than 2*pi when calculating 50*(2*pi/50). That would make the circle appear close to complete, but I'd rather make sure without counting on that. \$\endgroup\$
    – ilkkachu
    Oct 21, 2016 at 23:39
1
\$\begingroup\$
#include "SDL.h"
#include <math.h>
#include <stdio.h>

#define SCR_WDT  1280
#define SCR_HGT  960

const int SCR_CEN_X = SCR_WDT / 2;
const int SCR_CEN_Y = SCR_HGT / 2;

void DrawCircle( SDL_Renderer *r );

int main ( int argc, char *argv[] )
{
    SDL_Init ( SDL_INIT_VIDEO ); 
    SDL_Window *window = SDL_CreateWindow ( "Drawing a Circle", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCR_WDT, SCR_HGT, 0 );
    SDL_Renderer *renderer = SDL_CreateRenderer ( window, -1, SDL_RENDERER_SOFTWARE );

    bool is_running = true;
    while ( is_running )
    {
        SDL_Event event;
        if ( SDL_PollEvent( &event ))
        {
            if ( event.type == SDL_QUIT )
            {
                is_running = false;
            }   
        }

        SDL_RenderClear ( renderer );

        DrawCircle( renderer );

        SDL_SetRenderDrawColor ( renderer, 0, 0, 0, 255 );
        SDL_RenderPresent ( renderer ); 
    }

    SDL_Quit ();

return 0; 
}

void DrawCircle( SDL_Renderer *r )
{
    int radius = 200; 
    int new_x = 0;
    int new_y = 0;
    int old_x =  SCR_CEN_X + 200;
    int old_y = SCR_CEN_Y;
    float step = (M_PI * 2) / 50; 

    SDL_SetRenderDrawColor ( r, 255, 255, 255, 255 );

    for ( float theta = 0; theta <= (M_PI * 2); theta += step )
    {
        new_x = SCR_CEN_X + (radius * cos ( theta ));
        new_y = SCR_CEN_Y - (radius * sin ( theta ));   

        SDL_RenderDrawLine ( r, old_x, old_y, new_x, new_y );

        old_x = new_x;
        old_y = new_y;
    }

    new_x =  SCR_CEN_X + (radius * cos ( 0 ));
    new_y =  SCR_CEN_Y - (radius * sin ( 0 ));  
    SDL_RenderDrawLine( r, old_x, old_y, new_x, new_y );

}

The Circle struct has been removed and all of the circles variables are now stored in the DrawCircle function.

        int old_x =  SCR_CEN_X + 200;
        int old_y =  SCR_CEN_Y;

The old_x and old_y are now being set as before they where just zero producing the off by one error that @ikkachu pointed out.

        new_x =  SCR_CEN_X + (radius * cos ( 0 ));
        new_y =  SCR_CEN_Y - (radius * sin ( 0 ));  
        SDL_RenderDrawLine( r, old_x, old_y, new_x, new_y );

After I drop out of the loop, I'm completing the circle by drawing a final line starting from the last of the x and y and continuing at the appropriate angle.

\$\endgroup\$
1
  • \$\begingroup\$ I like your approach! And I have upvoted you, but now how to fill that circle? \$\endgroup\$
    – navy1978
    Nov 23, 2019 at 23:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.