Search code examples
c++graphicsgeometrysdlsdl-2

How would I remove these gaps in a circle drawn in SDL using the Midpoint Circle Algorithm?


I'm trying to create a rather simple game which largely involves drawing circles with SDL2. I discovered SDL lacks and built-in method to draw circles to an SDL_Renderer, but upon searching for a method, I discovered this helpful answer which details using the Midpoint Circle Algorithm to accomplish this. Since I wanted a filled circle, I wrote a rather simple function that just draws a lot of slightly smaller circles to give the appearance of a filled circle. Unfortunately, this resulted in circles being drawn with gaps that form a sort of 'X' pattern on the circle, as shown:Image of red circle on black background with some gaps that form an 'X' shape Here is my draw_hollow_circle function:

void draw_hollow_circle(SDL_Renderer *renderer, int centerx, int centery, int radius)
{
  // Draws a hollow circle with the given position and radius

  const int diameter = (radius * 2);

  int x = radius - 1;
  int y = 0;
  int tx = 1;
  int ty = 1;
  int error = tx - diameter;

  while (x >= y)
  {
    // Each renders an octant of the circle
    SDL_RenderDrawPoint(renderer, centerx + x, centery + y);
    SDL_RenderDrawPoint(renderer, centerx + x, centery - y);
    SDL_RenderDrawPoint(renderer, centerx - x, centery + y);
    SDL_RenderDrawPoint(renderer, centerx - x, centery - y);
    SDL_RenderDrawPoint(renderer, centerx + y, centery - x);
    SDL_RenderDrawPoint(renderer, centerx + y, centery + x);
    SDL_RenderDrawPoint(renderer, centerx - y, centery - x);
    SDL_RenderDrawPoint(renderer, centerx - y, centery + x);

    if (error <= 0)
    {
      ++y;
      error += ty;
      ty += 2;
    }

    if (error > 0)
    {
      --x;
      tx += 2;
      error += (tx - diameter);
    }
  }
}

And here is my draw_circle function:

void draw_circle(SDL_Renderer *renderer, int x, int y, int radius, int r, int g, int b)
{
  // Draws a filled circle with the given position, radius, and color

  SDL_SetRenderDrawColor(renderer, r, g, b, SDL_ALPHA_OPAQUE);

  // Draw a lot of slightly smaller hollow circles to give the impression of a filled circle
  while (radius >= 0)
  {
    draw_hollow_circle(renderer, x, y, radius);
    --radius;
  }
}

Now, this is rather annoying, and I would like a method of avoiding such gaps and getting just a pure red circle, but I have unfortunately not been able to find any method of doing so. I tried a different method involving drawing many radii going from the center of the circle to the edge, but this resulted in a similar problem, albeit with the gaps in slightly different places. Any type of answer would be fine, be it an algorithm better suited for filled circles, an error in the math of my code, etc.


Solution

  • The holes are artifacts of round-off errors. Precise positioning of each point would use real numbers (think floating point), but pixel coordinates must be integers, hence the rounding. Yes, you get similar artifacts when drawing diagonal lines for a similar reason. The only lines that could be drawn without rounding of some sort are horizontal, vertical, those with slope +1, and those with slope −1.

    A simple approach to fill the circle is to draw rectangles instead of points. Each iteration of the loop in draw_hollow_circle draws eight points. The first four would be the corners of one rectangle, while the second four form another rectangle. Try drawing those two rectangles instead of the eight points. (You no longer need to iterate over radii.)

    (Stick to fully opaque colors for this since many of the points will be drawn multiple times. That would interfere with partial transparency.)