[SDL] "Perfect" circles
Christophe Cavalaria
chris.cavalaria at free.fr
Thu Nov 9 11:07:35 PST 2006
xEsk PiV wrote:
> Hello,
>
> First, thank you to all for your answars! I'm so glad :)
>
> Second, I made this example to show you how I see the results (I build
> it on Windows, my main computer where I work is broken...).
>
> http://img73.imageshack.us/img73/2516/demo01lf8.png
> http://img490.imageshack.us/img490/3441/demo02js5.png
>
> Bye and have a nice day! :D
>
> xEsk.
>
> ---------
>
> Source code:
>
> #include <math.h>
> #include <SDL/SDL.h>
>
> float pre_cos[360];
> float pre_sin[360];
> bool is_pre_cos_sin_init = false;
>
> const float PI = 3.14159265358979323846;
>
> float RadToDeg(float rad) { return rad * 180 / PI; }
> float DegToRad(float deg) { return deg * PI / 180; }
>
> void init_pre_math()
> {
> for(int i = 0; i <= 360; i++)
> {
> pre_cos[i] = cos(DegToRad(i));
> pre_sin[i] = sin(DegToRad(i));
> }
>
> is_pre_cos_sin_init = true;
> }
>
> SDL_Rect advance(SDL_Rect pos, float angle, int units)
> {
> if (!is_pre_cos_sin_init) init_pre_math();
>
> int ang = static_cast<int>(-angle);
> ang = (ang < 0) ? (ang % 360) + 360: ang % 360;
>
> pos.x = pos.x + static_cast<Sint16>(units * pre_cos[ang]);
> pos.y = pos.y + static_cast<Sint16>(units * pre_sin[ang]);
>
> return pos;
> }
>
> /*
> This function is a modification of
> "int _putPixelAlpha(SDL_Surface * surface, Sint16 x, Sint16 y,
> Uint32 color, Uint8 alpha);"
>
> extracted from:
> SDL_gfxPrimitives.h - Graphics primitives for SDL surfaces
>
> LGPL (c) A. Schiffler
> */
>
> /* Defines for pixel clipping tests (SDL_gfxPrimitives) */
>
> #define clip_xmin(surface) surface->clip_rect.x
> #define clip_xmax(surface) surface->clip_rect.x+surface->clip_rect.w-1
> #define clip_ymin(surface) surface->clip_rect.y
> #define clip_ymax(surface) surface->clip_rect.y+surface->clip_rect.h-1
>
> bool putPixel(SDL_Surface* dst, Sint16 x, Sint16 y, Uint32 color,
> const Uint8 alpha)
> {
> /* Lock the surface */
> if (SDL_MUSTLOCK(dst))
> if (SDL_LockSurface(dst) < 0)
> return false;
>
> Uint32 R, G, B, A = 0;
> Uint32 Rmask = dst->format->Rmask,
> Gmask = dst->format->Gmask,
> Bmask = dst->format->Bmask,
> Amask = dst->format->Amask;
>
> /* First Check if this new pixel is in the surface */
> if (x >= clip_xmin(dst) && x <= clip_xmax(dst) && y >=
> clip_ymin(dst) && y <= clip_ymax(dst))
> {
> if (alpha == 255)
> *((Uint16 *)dst->pixels + y * dst->pitch / 2 + x) = color;
> else
> {
> Uint16* pixel = (Uint16*)dst->pixels + y* dst->pitch / 2 + x;
> Uint32 dc = *pixel;
>
> R = ((dc & Rmask) + (((color & Rmask) - (dc & Rmask)) * alpha >>
> 8)) & Rmask;
> G = ((dc & Gmask) + (((color & Gmask) - (dc & Gmask)) * alpha >>
> 8)) & Gmask;
> B = ((dc & Bmask) + (((color & Bmask) - (dc & Bmask)) * alpha >>
> 8)) & Bmask;
>
> if (Amask)
> A = ((dc & Amask) + (((color & Amask) - (dc & Amask)) * alpha
>>> 8)) & Amask;
>
> *pixel = R | G | B | A;
> }
> return true;
> }
>
> /* Unlock the surface */
> if (SDL_MUSTLOCK(dst))
> SDL_UnlockSurface(dst);
>
> return true;
> }
>
> int main (int argc, char *argv[]){
> /* Initialize SDL */
> SDL_Init (SDL_INIT_VIDEO);
> atexit (SDL_Quit);
>
> /* Set 640x480 16-bits video mode */
> SDL_Surface *screen = SDL_SetVideoMode (320, 250, 16,
> SDL_SWSURFACE | SDL_DOUBLEBUF);
> /* white screen */
> SDL_FillRect(screen, NULL, SDL_MapRGB (screen->format, 255, 255,
> 255));
>
> SDL_Rect position;
> position.x = 160;
> position.y = 240;
>
> /* example: */
> for (int n = 0; n < 360; n++)
> {
> position = advance(position, n, 3);
> putPixel(screen, position.x, position.y, 0, 255);
> }
> /* Make sure everything is displayed on screen */
> SDL_Flip(screen);
>
> /* main loop */
> int done = 0;
> while (!done)
> {
> SDL_Event event;
> /* Check for events */
> while (SDL_PollEvent (&event))
> {
> switch (event.type)
> {
> case SDL_KEYDOWN:
> break;
> case SDL_QUIT:
> done = 1;
> break;
> default:
> break;
> }
> }
> }
> return 0;
> }
It's a miracle you even got something that looks somewhat like a circle to
draw :)
Here :
> pos.x = pos.x + static_cast<Sint16>(units * pre_cos[ang]);
> pos.y = pos.y + static_cast<Sint16>(units * pre_sin[ang]);
You modify inplace the rectangle passed as an argument. In that situation,
returning pos is useless. But anyway, let's admit that pos was initialised
with the center of the circle before the function call.
Here :
> for (int n = 0; n < 360; n++)
> {
> position = advance(position, n, 3);
> putPixel(screen, position.x, position.y, 0, 255);
> }
You never reset position to the center of the circle between calls. And at
that point I've understood what you were trying to do. Now, I have to admit
that such algorithm will indeed be useful to display a circle, but it is a
strange way to do it. And since position stores the pixel coordinates in an
integer, you do not have enouth accuracy to make it work :) I can only
suspect that your previous version stored the pixel coordinates in a float.
Much easier is to recompute the new pixel position from the center between
each call. Use that formula instead :
pos.x = center.x + pre_cos[ang] * radius
pos.y = center.y + pre_cos[ang] * radius
and loop with ang going from 0 to 360.
All that reminds me of the time I used Locomotive Basic on a CPC to draw
things :)
More information about the SDL
mailing list