Drawing and filling a Circle

1

I just started with SDL in c++ and VS 2013 Community. I want to draw a circle and so i searched on Google and found http://content.gpwiki.org/index.php/SDL:Tutorials:Drawing_and_Filling_Circles. But as I tried to implement it in a seperate fill_circle.cpp file, include this in my main.cpp and call the function, I get the Error that said function fill_circle() is already defined in fill_circle.obj and that there are conflicts with other libs. So I tried to implement the drawning function directly into my main.cpp but i get a similar error, saying that void __cdecl fill_circle(struct SDL_Surface *,int,int,int,unsigned int) is already defined in fill_circle.obj.

I dont know what to do with those errors and hope someone of you can help me :)

EDIT: After completly removing the fill_circle.cpp and debug folder and implementing the Function in main.cpp the programm will compile but throw an error at runtime.

My main.cpp:

    #include <SDL.h>
#include <iostream>
#include <cmath>

void fill_circle(SDL_Surface *surface, int cx, int cy, int radius, Uint32 pixel)
{
    static const int BPP = 4;

    double r = (double)radius;

    for (double dy = 1; dy <= r; dy += 1.0)
    {
        // This loop is unrolled a bit, only iterating through half of the
        // height of the circle.  The result is used to draw a scan line and
        // its mirror image below it.

        // The following formula has been simplified from our original.  We
        // are using half of the width of the circle because we are provided
        // with a center and we need left/right coordinates.

        double dx = floor(sqrt((2.0 * r * dy) - (dy * dy)));
        int x = cx - dx;

        // Grab a pointer to the left-most pixel for each half of the circle
        Uint8 *target_pixel_a = (Uint8 *)surface->pixels + ((int)(cy + r - dy)) * surface->pitch + x * BPP;
        Uint8 *target_pixel_b = (Uint8 *)surface->pixels + ((int)(cy - r + dy)) * surface->pitch + x * BPP;

        for (; x <= cx + dx; x++)
        {
            *(Uint32 *)target_pixel_a = pixel;
            *(Uint32 *)target_pixel_b = pixel;
            target_pixel_a += BPP;
            target_pixel_b += BPP;
        }
    }
}

int main(int argc, char *argv[])
{
    //Main loop flag
    bool b_Quit = false;
    //Event handler 
    SDL_Event ev;
    //SDL window
    SDL_Window *window = NULL;

    SDL_Surface *windowSurface;

    if (SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        std::cout << "Video Initialisation Error: " << SDL_GetError() << std::endl;
    }
    else
    {
        window = SDL_CreateWindow("SDL_Project", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_SHOWN);
        if (window == NULL)
        {
            std::cout << "Window Creation Error: " << SDL_GetError() << std::endl;
        }
        else
        {
            windowSurface = SDL_GetWindowSurface(window);

            fill_circle(windowSurface, 10, 10, 20, 0xffffffff);

            //Main loop
            while (!b_Quit)
            {
                //Event Loop
                while (SDL_PollEvent(&ev) != 0)
                {
                    //Quit Event
                    if (ev.type == SDL_QUIT)
                    {
                        b_Quit = true;
                    }
                }
                SDL_UpdateWindowSurface(window);
            }

        }
    }

    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}
c++
drawing
sdl
geometry
asked on Stack Overflow Feb 5, 2015 by Phorskin • edited Feb 5, 2015 by genpfault

3 Answers

1

A circle with a radius of 20 with its' center at (10, 10) like you have:

fill_circle(windowSurface, 10, 10, 20, 0xffffffff);

Will make you address pixels outside of the allocated surface

    ... = (Uint8 *)surface->pixels + ((int)(cy + r - dy)) * surface->pitch + x * BPP;
    ... = (Uint8 *)surface->pixels + ((int)(cy - r + dy)) * surface->pitch + x * BPP;

This should cause a crash.

I have used the same algorithm in some projects and with SDL 1.2 it is not very safe the way it is written.

answered on Stack Overflow Feb 5, 2015 by Martin G
1

This can help you:

void draw_circle(SDL_Point center, int radius, SDL_Color color)
{
    SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
    for (int w = 0; w < radius * 2; w++)
    {
        for (int h = 0; h < radius * 2; h++)
        {
            int dx = radius - w; // horizontal offset
            int dy = radius - h; // vertical offset
            if ((dx*dx + dy*dy) <= (radius * radius))
            {
                SDL_RenderDrawPoint(renderer, center.x + dx, center.y + dy);
            }
        }
    }
}
answered on Stack Overflow Jan 27, 2017 by Ryozuki
1

i just entered here to search for an idea to draw a filled Circle and tried Ryozuki solution but its cost is inmense so i present here an alternative.

void TextureManager::fillCircle(const fig::cSphere _dst){
    SDL_SetRenderDrawColor(Game::renderer, 0x00, 0x00, 0xff, 0xff);

    int p = std::sqrt(_dst.r * 2) + 4;
    for (unsigned char ii = 0; ii < p; ii++) {
        float angle = 2 * M_PI / p;
        fillTriangle( 
            fig::cTria{
            {
                _dst.pos.x + _dst.r*std::cos(ii * angle),
                _dst.pos.y + _dst.r*std::sin(ii * angle)
            },
            {
                _dst.pos.x + _dst.r*std::cos((ii + 1) * angle),
                _dst.pos.y + _dst.r*std::sin((ii + 1) * angle)
            },
            _dst.pos }
        );
    };

    SDL_SetRenderDrawColor(Game::renderer, 0xaa, 0xff, 0xaa, 0xff);
}

You, of course, will need to create a fillTriangle() function and this will help with that:

void TextureManager::fillTriangle(const fig::cTria dst){
    fnc::small drawable = 0;

    for (fnc::small i = 0; i < 3; i++) 
        if (dst.points[i]->x == dst.points[(i + 1) % 3]->x) { drawable = (i + 2) % 3 + 1;  break; }
        else if (dst.points[i]->y == dst.points[(i + 1) % 3]->y) { drawable = (i + 2) % 3 + 4; break; }
    
    if (drawable == 0) {
        std::pair<fig::cTria, fig::cTria> t = dst.subdivide(
            dst.getboundary().siz.x < dst.getboundary().siz.y
        );
        fillTriangle(t.first);
        fillTriangle(t.second);
    }
    else if (drawable < 4){
        short dx = -dst.points[drawable - 1]->x + dst.points[drawable % 2]->x;
        SDL_SetRenderDrawColor(Game::renderer, 0xaa, 0xaa, 0xff, 0xff);
        for (short i = 0; true; i = fnc::aproach((float)i, 1, dx)) {
            SDL_RenderDrawLine(
                Game::renderer,
                dst.points[drawable - 1]->x + i,
                fnc::lerp(dst.points[drawable - 1]->y, dst.points[(drawable + 1) % 3]->y, (float)i / (float)dx),
                dst.points[drawable - 1]->x + i,
                fnc::lerp(dst.points[drawable - 1]->y, dst.points[drawable % 3]->y, (float)i / (float)dx)
            );
            if (i == dx) break;
        }
        SDL_SetRenderDrawColor(Game::renderer, 0xaa, 0xff, 0xaa, 0xff);
    }
    else {
        drawable -= 3;

        short dy = -dst.points[drawable - 1]->y + dst.points[drawable % 2]->y;
        SDL_SetRenderDrawColor(Game::renderer, 0xaa, 0xaa, 0xff, 0xff);
        for (short i = 0; true; i = fnc::aproach((float)i, 1, dy)) {
            SDL_RenderDrawLine(
                Game::renderer,
                fnc::lerp(dst.points[drawable - 1]->x, dst.points[(drawable + 1) % 3]->x, (float)i / (float)dy),
                dst.points[drawable - 1]->y + i,
                fnc::lerp(dst.points[drawable - 1]->x, dst.points[drawable % 3]->x, (float)i / (float)dy),
                dst.points[drawable - 1]->y + i
            );
            if (i == dy) break;
        }
        SDL_SetRenderDrawColor(Game::renderer, 0xaa, 0xff, 0xaa, 0xff);
    }
}

and about the weird classes, they're:

using fnc::small = unsigned char;
using fnc::ushort = unsigned short;
using fnc::uint = unsigned int;

and also, the fig::cTria::subdivide() just divide the triangle in two triangles whose area can be "integrable". I mean, in a way there is a line that's either totally horizontal or totally vertical. the fig::cTria::boudary() returns the fig::cRect in which the triangle is inscribed.

here, i've found another way to solve this problem

void TextureManager::fillCircle(const fig::cSphere _dst){
    SDL_SetRenderDrawColor(Game::renderer, 0x00, 0x00, 0xff, 0xff);

    for (fnc::uint dx = 0; dx < _dst.r * 2; dx++) {
        SDL_RenderDrawLine(Game::renderer,
            _dst.pos.x - _dst.r + dx,
            _dst.pos.y + std::sqrtf(std::powf(_dst.r, 2) - std::powf(dx - _dst.r, 2)),
            _dst.pos.x - _dst.r + dx,
            _dst.pos.y - std::sqrtf(std::powf(_dst.r, 2) - std::powf(dx - _dst.r, 2))
        );
    }
    SDL_SetRenderDrawColor(Game::renderer, 0xaa, 0xff, 0xaa, 0xff);
}

And i think it's faster. Hope this helps somebody. if someone have suggestions, they'll be appreciated

answered on Stack Overflow Jul 28, 2020 by Sebastian R. • edited Sep 15, 2020 by Sebastian R.

User contributions licensed under CC BY-SA 3.0